7

看完这篇文章还不会call/apply/bind的来找我。

 3 years ago
source link: https://segmentfault.com/a/1190000006993545
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

看完这篇文章还不会call/apply/bind的来找我。

发布于 2016-09-24

先从一个小题目开始吧:

要实现一个加法函数,这个时候向函数当中传递个数大于0的若干个整形数据,求所有这些数据的和。

  • Function.prototype.call

  • Function.prototype.apply

  • Function.prototype.bind

其中call方法:

    var personA = {
        name: 'XL',
        sayName: function (hobby){
            console.log(this.name + ' likes ' + hobby);
        } 
    };
    
    personA.sayName('basketball');   // 'XL likes basketball'
    
    var personB = {
        name: 'xl'
    }
    
    personA.sayName.call(personB, 'basketball');  // 'xl likes basketball'
    personA.sayName.apply(personB, ['basketball']); // 'xl likes basketball'
    
    

call和apply的区别就在于传递方式的不同,call在接收指定参数的形式是someMethod.call(obj, arg1, arg2);而apply在接收指定参数时的形式是someMethod.apply(obj, [arg1, arg2]).或者someMethod.apply(obj, arg1),但是这个arg1必须是一个类数组对象

其实想要真正掌握call/apply包括bind方法,首先必须搞清楚当一个函数/方法被调用的时候this的指向问题。 关于this的指向的问题请参照我的学习笔记

那么在这里call,apply,bind事实上都改变了函数/方法被调用时this的指向。

还是拿上面的例子来说:

    personA.sayName(‘basketball’); //调用sayName()这个方法的对象是personA,因此sayName()内部的this指向就是personA对象

换一种写法

    var sayName = personA.sayName('basketball');
    //这里将sayName方法挂载到了window对象上,即window.sayName = person.sayName();  这个时候调用sayName().此时this指向就是window对象

使用call/apply

    personA.sayName.call(personB, 'basketball');
    //本来sayName方法的this指向是personA对象,但是调用call后,this对象指向了personB对象。

如果大家这种写法看不习惯,那就换种方式来看:

    personA.sayName.call(personB, 'basketball') ===> personB.sayName('basketball');
    //从前面的一个形式变为后面一种形式,此时,sayName方法的this指向是personB对象了。

换一种方式书写后大家应该看的很清晰明了了吧?以后碰到call/apply调用的时候,换一种形式去理解,这样就很清晰了。

再比如大家经常看到的一种对于函数的arguments类数组对象的处理方式:

    function fn() {
        var args = Array.prototype.slice.apply(arguments); //这里将arguments这个类数组对象转化为一个数组 
    }
    
    //咱们再来转化下:
Array.prototype.slice.apply(arguments); ===>>> arguments.slice(); 
//因为arguments是类数组对象的原因,因此它可以直接调用slice方法;如果要截取数组的从第几位到第几位的数

Array.prototype.slice.apply(arguments, [0, 2]); ===>>> arguments.slice(0, 2);

握草,感觉编不下去了- -

其实将call/apply,换一种形式去看,是不是就和普通的方法调用一样一样的。

bind方法呢,起的作用和call,apply一样,都是改变函数/方法执行时,this的指向,确保这个函数/方法运行时this指向保持一致。
比如大家经常用到的setTimeout异步函数:

    var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }, 0);
        }
    }
    
    person.sayName();   //最后输出: undefined

这是因为setTimeout()这个异步函数调用的时候,内部的回调函数this的指向是window.但是在window对象上并未挂载name属性,因此最后输出undefined.

添加一行代码

    var name = 'XLLLL';
    var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }, 0);
        }
    }
    
    person.sayName();   //输出  ‘XLLLL’

为了避免在回调函数当中,this指向发生变化,所以大家都会这样处理:

    var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }.bind(this), 0);   //通过bind方法将this对象绑定为person。那么回调函数在执行的时候,this指向还是person。
        }
    }

可以用下面这段代码来简单模拟下bind方法内部的操作:

    Function.prototype.bind = function(obj) {
        var method = this;
        return function() {
            method.apply(obj, arguments);
        }
    }

还记得刚才给大家讲的将apply进行变换的形式吗?

    Function.prototype.bind = function(obj) {
        var method = this;
        return function() {
            obj.method(arguments);
        }
    }

大家应该看到了bindapply/call的区别了吧? bind方法是返回一个新的函数,但是这个函数比较特殊,这个函数的this对象已经被bind方法传入的第一个参数给绑定了.

比如我们可以使用bind方法来简写一个方法:

    function fn() {
        var hasOwnKey = Function.call.bind(Object.hasOwnProperty);
        
        for(var key in obj) {
            if(hasOwnKey(obj, key)) {
                //xxxx
            }
        }
    }

唉,真的编不下去了。大家看完之后应该已经懂了把? - -

还是不懂的话在评论区留言,我给大家解答。

哦,一开始那个题目的一种写法

    //要实现一个加法函数,这个时候向函数当中传递个数大于0的若干个整形数据,求所有这些数据的和。
    function add() {
        return Array.prototype.reduce.call(arguments, function(n1, n2) {
            return n1 + n2;
        });   
    }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK