this绑定番外篇之箭头函数绑定
我们在前几章说this永远指向最后调用它的那个对象。 但对于箭头函数来说不是的,它里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时。
它里面的this是由外层作用域来决定的怎么理解呢?首先箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。
var name = 'window'
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this.name)
}
}
var obj2 = {
name: 'obj2',
foo: () => {
console.log(this.name)
}
}
obj1.foo()
obj2.foo()
答案:
obj1
window
不使用箭头函数的obj1.foo()是由obj1调用的,所以this.name为obj1。 使用箭头函数的obj2.foo()的外层作用域是window,所以this.name为window。
var obj = {
name: 'obj',
foo1: () => {
console.log(this.name)
},
foo2: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var name = 'window'
obj.foo1()
obj.foo2()()
答案是
window
obj
obj
对于obj.foo1()函数的调用,它的外层作用域是window,对象obj当然不属于作用域了(我们知道作用域只有全局作用域window和局部作用域函数)。所以会打印出window
obj.foo2()(),首先会执行obj.foo2(),这不是个箭头函数,所以它里面的this是调用它的obj对象,因此打印出obj,而返回的匿名函数是一个箭头函数,它的this由外层作用域决定,那也就是函数foo2咯,那也就是它的this会和foo2函数里的this一样,就也打印出了obj。
var name = 'window'
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2',
foo: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var obj3 = {
name: 'obj3',
foo: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj4 = {
name: 'obj4',
foo: () => {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
obj1.foo()() // 'obj1' 'window'
obj2.foo()() // 'obj2' 'obj2'
obj3.foo()() // 'window' 'window'
obj4.foo()() // 'window' 'window'
obj1.foo()()两层都是普通函数,类似于题目4.6,分别打印出obj1和window。
obj2.foo()()外层为普通函数,内层为箭头,类似于题目7.1,都是打印出obj2。
obj3.foo()()外层为箭头函数,内层为普通函数,箭头函数的this由外层作用域决定,因此为window,内层普通函数由调用者决定,调用它的是window,因此也为window。
obj4.foo()()两层都是箭头函数,第一个箭头函数的this由外层作用域决定,因此为window,第二个箭头函数的this也由外层作用域决定,它的外层作用域是第一个箭头函数,而第一个箭头函数的this是window,因此内层的this也是window。
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = () => {
console.log(this.name)
}
}
var person2 = {
name: 'person2',
foo2: () => {
console.log(this.name)
}
}
var person1 = new Person('person1')
person1.foo1()
person1.foo2()
person2.foo2()
答案:
'person1'
'person1'
'window'
person1.foo1()是个普通函数,this由最后调用它的对象决定,即person1。
person1.foo2()为箭头函数,this由外层作用域决定,且指向函数定义时的this而非执行时,在这里它的外层作用域是函数Person,且这个是构造函数,并且使用了new来生成了对象person1,所以此时this的指向是为person1。
person2.foo2()字面量创建的的对象person2中的foo2是个箭头函数,由于person2是直接在window下创建的,你可以理解为它所在的作用域就是在window下,因此person2.foo2()内的this应该是window。
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
return function () {
console.log(this.name)
}
}
this.foo2 = function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
this.foo3 = () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
this.foo4 = () => {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
person1.foo1()() // 'person1' 'window'
person1.foo2()() // 'person1' 'person1'
person1.foo3()() // 'person1' 'window'
person1.foo4()() // 'person1' 'person1'
person1.foo1()()两层都是普通函数,这个不再重复说了,打印出person1和window。(类似题目6.2)
person1.foo2()()第一层普通函数,它的this是由最后调用它的对象决定也就是person1,第二层为箭头函数,它的this由外层作用域决定,也就是foo2这个函数,因此也为person1。
person1.foo3()()第一层为箭头函数,this由外层作用域决定,因此为person1,第二层为普通函数,由最后调用者决定,因此为window。
person1.foo4()()两层都是箭头函数,this由外层作用域决定,所以都是person1。
箭头函数的this无法通过bind、call、apply来直接修改,但是可以通过改变作用域中this的指向来间接修改。
var name = 'window'
var obj1 = {
name: 'obj1',
foo1: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
},
foo2: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.foo1.call(obj2)()
obj1.foo1().call(obj2)
obj1.foo2.call(obj2)()
obj1.foo2().call(obj2)
答案:
// 'obj2' 'obj2'
// 'obj1' 'obj1'
// 'window' 'window'
// 'window' 'obj2'
obj1.foo1.call(obj2)()第一层为普通函数,并且通过.call改变了this指向为obj2,所以会打印出obj2,第二层为箭头函数,它的this和外层作用域中的this相同,因此也是obj2。
obj1.foo().call(obj2)第一层打印出obj1,第二层为箭头函数,使用了.call想要修改this的指向,但是并不能成功,因此.call(obj2)对箭头函数无效,还是打印出obj1。
obj1.foo2.call(obj2)()第一层为箭头函数,并且想要通过.call(obj2)改变this指向,但是无效,且它的外层作用域是window,所以会打印出window,第二层为普通函数,this是最后调用者window,所以也会打印出window。
obj1.foo2().call(obj2)第一层为箭头函数,外层作用域是window,打印出window,第二层为普通函数,且使用了.call(obj2)来改变this指向,所以打印出了obj2。
在这道题中,obj1.foo1.call(obj2)()就相当于是通过改变作用域间接改变箭头函数内this的指向。
总结 OK👌,来总结一下箭头函数需要注意的点吧:
它里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时 字面量创建的对象,作用域是window,如果里面有箭头函数属性的话,this指向的是window 构造函数创建的对象,作用域是可以理解为是这个构造函数,且这个构造函数的this是指向新建的对象的,因此this指向这个对象。 箭头函数的this是无法通过bind、call、apply来直接修改,但是可以通过改变作用域中this的指向来间接修改。
优点
箭头函数写代码拥有更加简洁的语法(当然也有人认为这是缺点) this由外层作用域决定,所以在某些场合我们不需要写类似const that = this这样的代码
避免使用的场景
1.使用箭头函数定义对象的方法
let obj = {
value: 'LinDaiDai',
getValue: () => console.log(this.value)
}
obj.getValue() // undefined
2.定义原型方法
function Foo (value) {
this.value = value
}
Foo.prototype.getValue = () => console.log(this.value)
const foo1 = new Foo(1)
foo1.getValue() // undefined
3.构造函数使用箭头函数
const Foo = (value) => {
this.value = value;
}
const foo1 = new Foo(1)
// 事实上直接就报错了 Uncaught TypeError: Foo is not a constructor
console.log(foo1);
4.作为事件的回调函数
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
var circle = {
radius: 10,
getRadius() {
console.log(this.radius);
}
};
circle.getRadius(); // 打印 10
// 使用临时变量self
var circle = {
radius: 10,
outerDiameter() {
var self = this;
var innerDiameter = function() {
console.log(2 * self.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20
// innerDiameter函数中的this是window
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(this === window);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印true
// 使用普通函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印NaN
// 使用.bind(this)
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function() {
console.log(2 * this.radius);
};
innerDiameter = innerDiameter.bind(this);
innerDiameter();
}
};
circle.outerDiameter(); // 打印20
// 使用箭头函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = () => {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20