先从一个小题目开始吧:
要实现一个加法函数,这个时候向函数当中传递个数大于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);
}
}
大家应该看到了bind
和apply/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;
});
}