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

先从一个小题目开始吧:javascript

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

  • Function.prototype.callsegmentfault

  • Function.prototype.apply数组

  • Function.prototype.bindapp

其中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的指向。this

仍是拿上面的例子来讲:prototype

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;
        });   
    }
相关文章
相关标签/搜索