老生常谈的this
1. 前言
JavaScript中的this可能是当年设计的时候存在着设计缺陷,在ES6中能使用()=>
这个高端的箭头函数就尽量使用箭头函数,箭头函数在其他语言中还有个高大上的名字Lambda
表达式。
PS:nodejs
环境中没有window
对象
2. this指向
对于this的指向,我的理解就是记住一句话,**如果没有使用apply和call还有箭头函数的情况下,this指向最后一次调用它的对象,如果最后一次调用它的是函数,则在严格模式指向undefined
非严格模式下指向全局变量window
**。
下面的例子全部为非严格模式
var a = 1;
var obj = {
a: 2,
b: function() {
return this.a;
}
};
console.log(obj.b()); //2
上面的例子应该很清晰,因为最后一次调用b
方法的是obj
这个对象,而对象obj
里面a的值为2,所以这个例子会输出2。
那么我们加大一下难度看下面的例子
var a = 1;
var obj = {
a: 2,
b: function() {
return this.a;
}
};
var t = obj.b;
console.log(t());
console.log(obj.b());
console.log(t() === obj.b());
console.log(t === obj.b);
这个时候我们新加入一个变量t
,将b
方法赋值给t
,大家思考一下这个时候函数t
会输出什么。
神奇的事情发生了
console.log(t()); //1
console.log(obj.b()); //2
console.log(t() === obj.b()); //false
console.log(t === obj.b); //true
函数t
输出的结果是1。
因为这次调用方法的是函数t
而他的对象为window
,因为对象window
上的a
值为1,故t
的最后输出结果为1。
下面这个例子也同样
var a = 1;
var obj = {
a: 2,
b: function() {
function fun() {
return this.a;
}
console.log(fun());
}
};
obj.b(); //1
因为调用fun
方法的是方法b
,这时最后一次调用fun
的不为对象,则this指向全局变量window
3. 箭头函数
- 箭头函数会捕获其所在上下文的
this
值,作为自己的this
值 - 箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为
undefined
。
3.1 解决的痛点
在ES5
语法中使用this有时候会很难以预测this的指向,从而产生bug,这个问题到了现在框架时代大量通过类进行封装时变得尤为明显。如果学习过react,就会发现将父组件的函数传递给子组件使用时,this指向总是个头疼的问题。
虽然使用call,apply,bind可以解决这部分痛点,但是需要额外的代码,不仅增大了工作量,而且增加了维护成本。
3.2 简单题目
var a = 1;
var obj = {
a: 2,
b: () =>{
return this.a;
}
};
console.log(obj.b()); //1
因为b是通过箭头函数进行声明,则它的this就指向了obj
的this,而obj
的this就是全局变量window
3.3 中等题目
var a = 1;
var obj = {
a: 2,
b: function () {
var fun = () => {
return this.a;
};
console.log(fun());
},
};
obj.b(); //2
因为箭头函数会找上下文的this,所以这里找到的上文就是方法b
的this,即为对象obj
var a = 1;
var obj = {
a: 2,
b: () => {
var fun = () => {
return this.a;
};
console.log(fun());
},
};
obj.b(); //1
箭头函数是没有this的,这个时候方法fun
就逐层寻找this,最终找到的是对象obj
的this
4. call,apply,bind
apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。
call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
bind除了返回是函数以外,它的参数和call一样。
4.1 箭头函数
this是不能改变的,所以对箭头函数使用call,apply,bind是没有效果的
var a = 1;
var obj = {
a: 2,
b: function () {
var fun = () => {
return this.a;
};
console.log(fun.call(a));
},
};
obj.b(); //2
4.2 特殊情况
var a = 1;
var obj = {
a: 2,
b: function () {
var fun = () => {
return this.a;
};
console.log(fun());
},
};
var obj1 = {
a: 3,
};
obj.b.call(obj1); //3
箭头函数的this虽然不能被改变,但是可以改变它上下文的this。
5. 一大堆题
既然上面都讲了那么多,那么我们来看一下下面的题:
不知道是哪个大神想的题目,反正就是不让人好过。┓( ´∀` )┏
var age = 10;
var person = {
age: 20,
getAge() {
var age = 30;
return this.age; //20
}
};
console.log(age, age * 2); //10 20
console.log(person.getAge()); //20
var b = person.getAge;
console.log(b()); //10
console.log(person.getAge()); //20
console.log((1, person.getAge)()); //10
console.log((1, person.getAge.bind(person))()); //20
console.log((person.getAge, person.getAge)()); //10
console.log((person.getAge = person.getAge)()); //10
console.log(person.getAge.call()); //10
console.log(person.getAge.call(person)); //20
function getAge2() {
this.age = 40;
console.log(person.getAge());
}
getAge2(); //20
console.log(age); //node环境和浏览器环境不一样,因为浏览器环境中有window对象
function getAge3() {
this.age = 50;
this.getAge4 = () => {
console.log(person.getAge.call(this)); //50
};
}
console.log(new getAge3().getAge4());
console.log(age); //10
function getAge4() {
this.age = 60;
this.getAge5 = () => {
console.log(person.getAge.call(this)); //60
};
}
console.log(new getAge4().getAge5()); //undefined
console.log(age); //10
var age2 = 10;
var person2 = {
age2: 20,
getAge2: () => {
var age2 = 30;
return this.age2;
}
};
console.log(person2.getAge2.call()); //10
console.log(person2.getAge2.call(person2)); //10
6. 写在最后
看了很多篇文章,对那些文章进行了总结,我也不能说我百分百懂了this,如果有错误的地方,欢迎在评论指出~
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!