一顿废话抛出问题
菜鸟的学习之路总是遇到各种各样菜得抠脚的问题。这不前两天碰到一个问题,在用jquery遍历数组的时候需要调用一个属性的时候却发现,this指向的是遍历的那个元素了而不是外部,故调取不到所需的属性。如下代码所示
var Page = function () {
this.temp = 'P';
this.things = [1, 2, 3];
this.show = function () {
$.each(this.things, function (index, value) {
// 此处this指的是value,即数组中的每个字符串,所以调取this.color失败,为undefined
console.log(this.temp + ' ' + value);
});
}
}
var p = new Page();
p.show();
复制代码
emmm……大概就是上面说的这个意思。
一些解决办法
下面介绍我想到的解决办法,先来点蠢的。
一个蠢办法
第一个想到的是将外部this定义为一个变量,以供使用,如下所示
var Page = function () {
this.temp = 'P';
this.things = [1, 2, 3];
this.show = function () {
// 定义一个变量
var that = this;
$.each(this.things, function (index, value) {
// 这里用新定义的变量代替
console.log(that.temp + ' ' + value);
});
}
}
var p = new Page();
p.show();
复制代码
这个办法可以解决,但是定义了一个变量,不行不行,太麻烦。
另一个办法
之后又想到了es6的箭头函数,箭头函数的this指向的是所处环境上下文的this值,所以应该可以结局,如下所示
var Page = function () {
this.temp = 'P';
this.things = [1, 2, 3];
this.show = function () {
// 箭头函数
$.each(this.things, (index, value) => {
console.log(this.temp + ' ' + value);
});
}
}
var p = new Page();
p.show();
复制代码
又一个办法之bind
众所周知,bind是可以改变函数体内this指向的。bind()方法会创建一个新函数,即绑定函数,当它被调用时,会以创建它时传入bind()方法的第一个参数作为this。如下所示
var Page = function () {
this.temp = 'P';
this.things = [1, 2, 3];
this.show = function () {
$.each(this.things, function (index, value) {
console.log(this.temp + ' ' + value);
}.bind(this));
// 此处的被传入bind方法的是外部的this, 这样当函数体内调取this时指向的就是外部的this
}
}
var p = new Page();
p.show();
复制代码
但是这里经过实验有个问题就是bind叠加绑定是没有作用的。要问为什么,那就是太菜了还不知道。
一顿偏题后的重点
前面说了一大串,对于本文标题所提到的内容只说了个bind(),接下来来说说call和apply这两个方法吧。与bind方法类似的是,它们的作用同样是改变函数体内的this指向的。
apply和call的用法
先上代码
var Person = function () {
this.name = '李雷';
// 定义一个打招呼的方法
this.sayHello = function () {
console.log('你好, 我是' + this.name);
}
}
var Student = function () {
this.name = '韩梅梅';
}
var p = new Person();
var s = new Student();
p.sayHello(); // 打印结果 "你好, 我是李雷"
p.sayHello.call(s); // 此处通过call改变了sayHello函数的上下文,即为s,所以打印结果为 "你好, 我是韩梅梅"
p.sayHello.apply(s); // 此处通过apply改变了sayHello函数的上下文,即为s,所以打印结果为 "你好, 我是韩梅梅"
复制代码
就如上面代码和注释当中所说的,Student里是没有定义sayHello这个方法的,所以当我们想要在s这个对象里使用sayHello方法时,就得向p这个对象去借用这个方法,因为p对象里有。此处call和apply就是改变了p.sayHello这个方法的上下文,将this指向了s对象,所以当sayHello函数打印到this.name时,就变成了’韩梅梅’。
apply和call的区别
那么问题又来了,在上面的代码中看似call和apply没啥区别,那为啥要两个呢。看下面一段代码
function add(a, b, c) {
return (a + b + c);
}
function callTest(a, b, c) {
return add.call(this, a, b, c); // 传参必须一一对应
}
function applyTest(a, b, c) {
return add.apply(this, [a, b, c]); // 传参以数组形式
}
console.log(callTest(1, 2, 3));
console.log(applyTest(1, 2, 3));
复制代码
由此可以看出,call需要把参数按顺序传递进去,而apply则是把参数放在数组里。因此要说适用条件的话,当你的参数是明确知道数量时可以用call,而不确定的时候则用apply,然后把参数push进数组传递进去。当参数数量不确定时,函数内部也可以通过arguments这个伪数组来遍历所有的参数。
再和bind来比比
call和apply的区别已经讲过了,那么它们二者和bind之间的区别又在哪儿呢。就像前面说过的,bind方法是创建了一个绑定函数,当它被调用时才会起到作用。所以大致可以理解为,call和apply是立即生效,而bind则是绑定了之后当其调用了之后才会生效。下面是一段进行验证的代码
var test1 = {
temp: 5
}
var test2 = {
show: function () {
console.log(this.temp);
}
}
test2.show.call(test1);
test2.show.apply(test1);
test2.show.bind(test1)(); // 此处明显看出是主动调用了该函数
复制代码
一顿总结
最后总结一下
1、三者的作用都是改变函数的上下文,即函数体内this指向的,类似于标题说的你的函数借我用用;
2、三者的第一个参数都是借用函数的主体,后续都可以继续传入参数;
3、bind是等待调用,而另外两个是立即调用。