zoukankan      html  css  js  c++  java
  • 02函数-02-方法 和 高阶函数

    1、方法

    1.1 方法的定义

    方法不就是函数?还是有点区别的,在一个对象中绑定函数,称为这个对象的方法
    var xiaoming = { 
        name: '小明',
        birth: 1990,
        age: function () {
            var y = new Date().getFullYear();
            return y - this.birth;
        }
    };
    
    xiaoming.age; // function xiaoming.age() 返回函数内容
    xiaoming.age(); // 2017年调用是27,第二年调用就变成28了

    1.2 this关键字

    上面的函数中用到了一个特殊的关键字,this,在一个方法内部,它始终指向当前对象,也就是xiaoming这个变量,所以this.birth可以拿到xiaoming的birth属性。

    然而这个this也可以说是JavaScript中设计的一个缺陷,为什么呢?结合以上,我们看下面的代码:
    var fn = xiaoming.age; // 先拿到xiaoming的age函数
    fn(); // NaN

    是不是觉得很奇怪,为什么会得到NaN?this在之前不是指向了xiaoming吗?

    实际上,this的指向是随情况而定的,如果我们使用 xiaoming.age()的方式,this才是正确指向xiaoming(如果普通函数没有写在对象里,实际上this又会指向window对象,即全局对象),所以,要保证this的指向正确,必须用obj.xxx()的形式调用方法

    诸如以下的方式也是不行的,this的指向只在age的方法中指向xiaoming,方法中嵌套是无效的,会重新指向全局对象window:
    var xiaoming = {
        name: '小明',
        birth: 1990,
        age: function () {
            function getAgeFromBirth() {
                var y = new Date().getFullYear();
                return y - this.birth;
            }
            return getAgeFromBirth();
        }
    };
    
    xiaoming.age();

    1.3 缺陷修复

    1.3.1 申请额外变量获取

    想要修复如此问题,只能再申请一个变量来捕获this:
    var xiaoming = {
        name: '小明',
        birth: 1990,
        age: function () {
            var that = this; // 在方法内部一开始就捕获this
            function getAgeFromBirth() {
                var y = new Date().getFullYear();
                return y - that.birth; // 用that而不是this
            }
            return getAgeFromBirth();
        }
    };
    
    xiaoming.age();

    1.3.2 apply和call方法

    或者使用函数本身的 apply 方法,来控制this的指向
    该方法有两个参数,第一个参数是需要绑定的this变量,第二个参数是数组Array,表示函数本身的参数:
    function getAge() {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
    
    var xiaoming = {
        name: '小明',
        birth: 1990,
        age: getAge
    };
    
    xiaoming.age(); // 25
    getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

    你可以这样理解,就类似于Java中类的非静态方法,它是属于对象的,不能直接使用,所以apply这里要求你定义两个东西,一个是对象,一个是必要参数。

    还有一个和apply()类似的方法是 call() ,不同的是,call() 的参数传入直接按顺序传入,不需要打包成Array:
    Math.max.apply(null, [3, 5, 4]); // 5
    Math.max.call(null, 3, 5, 4); // 5

    思维拓展:用apply动态改变函数的行为
    假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。
    最佳方案是用我们自己的函数替换掉默认的parseInt():
    var count = 0;
    var oldParseInt = parseInt; // 保存原函数
    
    window.parseInt = function () {
        count += 1;
        return oldParseInt.apply(null, arguments); // 调用原函数
    };
    
    // 测试:
    parseInt('10');
    parseInt('20');
    parseInt('30');
    count; // 3

    2、高阶函数

    我们知道函数实际也是一个对象,也可以i用变量指向函数,既然如此,那么我们完全可以将一个指向函数的变量a,作为另外一个函数的接收参数,这就是高阶函数:

    function add(x, y, f) {
        return f(x) + f(y);
    }
    
    当我们调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs:
    x = -5;
    y = 6;
    f = Math.abs;
    f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
    return 11;

    2.1 map

    map() --> 定义在JavaScript中的数组Array中,在map()中传入自己的函数,可以得到一个新的Array作为结果

    function pow(x) {
        return x * x;
    }
    
    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

    map作为高阶函数,实际上把运算规则抽象了,上例中,实际上你也可以分别遍历出元素,然后每次把元素进行函数运算后的结果push到一个新的Array里,最终把这个Array返回。

    所以map更能直观地表述:把f(x)作用在Array的每一个元素并把结果生成一个新的Array

    2.2 reduce

    reduce() --> 把函数作用在Array的[x1, x2, x3...]上,这个参数函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算
    e.g.
    [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

    比如对一个Array求和,如果用reduce实现的话就是:
    var arr = [1, 3, 5, 7, 9];
    arr.reduce(function (x, y) {
        return x + y;
    }); // 25
    
    //实际上就是1和3作为参数,带入函数运算得到4
    //作为结果的4再和下一个元素5带入函数继续运算得到结果9
    //循环,即最终得到25

    继续拓展一下思维,把[1, 3, 5, 7, 9]变换成整数13579:
    var arr = [1, 3, 5, 7, 9];
    arr.reduce(function (x, y) {
        return x * 10 + y;
    }); // 13579

    2.3 filter

    filter() --> 把传入的函数依次作用于Array的每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素
    //比如去掉一个Array中的偶数
    var arr = [1, 2, 4, 5, 6, 9, 10, 15];
    var r = arr.filter(function (x) {
        return x % 2 !== 0;
    });
    r; // [1, 5, 9, 15]

    为了去掉空字符串,利用filter和JS中对于boolean的判断:

    var arr = ['A', '', 'B', null, undefined, 'C', '  '];
    var r = arr.filter(function (s) {
        return s && s.trim(); // 注意:IE9以下的版本没有trim()方法
    });
    r; // ['A', 'B', 'C']

    filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身:
    var arr = ['A', 'B', 'C'];
    var r = arr.filter(function (element, index, self) {
        console.log(element); // 依次打印'A', 'B', 'C'
        console.log(index); // 依次打印0, 1, 2
        console.log(self); // self就是变量arr
        return true;
    });
    拓展思维:利用filter巧妙去重:
    var
        r,
        arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
    
    r = arr.filter(function (element, index, self) {
        return self.indexOf(element) === index;
    });
    
    alert(r.toString());

    2.4 sort

    sort作为排序的算法,想必其使用频率不言而喻,但是有一个大坑,那就是Array的sort()方法,默认是把所有元素转换为String再排序,这就会导致普通的数字排序中,例如10会排列在2的前面,不是因为10比2小,而是因为'10'中的字符'1'比‘2'的ASCII码小。另外,要注意的是,sort()方法是直接对原Array直接做出修改,返回当前的Array,如果你希望只是得到修改后的Array而不改变源Array,记得另起变量保存以后再执行sort。

    还好的是,sort()也是一个高阶函数,可以接收一个比较函数来实现自定义排序,类似Java中集合实现排序,元素要覆盖Comparable接口的CompareTo方法一样,所以在sort()中如果要实现按数字大小排序,可以这样写:
    var arr = [10, 20, 1, 2];
    arr.sort(function (x, y) {
        if (x < y) {
            return -1;
        }
        if (x > y) {
            return 1;
        }
        return 0;
    }); // [1, 2, 10, 20]

    还有诸如对字符串进行忽略大小写的字母排序:
    var arr = ['Google', 'apple', 'Microsoft'];
    arr.sort(function (s1, s2) {
        x1 = s1.toUpperCase();
        x2 = s2.toUpperCase();
        if (x1 < x2) {
            return -1;
        }
        if (x1 > x2) {
            return 1;
        }
        return 0;
    }); // ['apple', 'Google', 'Microsoft']


  • 相关阅读:
    递归函数
    js原生代码添加表格(行,列用户选择)
    Vue列表数组检测及列表过滤
    字符,图片及视频存储
    小程序js-api简介及操作
    小程序开发-了解
    外购入库单审核可以,删除失败,提示采购单据严格按照数量控制,收料通知单关联数量不能大或负数
    PDO基础应用之异常处理
    进程池用法
    [转]解决Error: That port is already in use.
  • 原文地址:https://www.cnblogs.com/deng-cc/p/6622483.html
Copyright © 2011-2022 走看看