总结了几道 this 指向的代码题,做过之后我发现我对 this 一无所知。
1.
var num = 1;
var myObject = {
num: 2,
add: function() {
this.num = 3;
(function() {
console.log(this.num);
this.num = 4;
})();
console.log(this.num);
},
sub: function() {
console.log(this.num)
}
}
myObject.add();
console.log(myObject.num);
console.log(num);
var sub = myObject.sub;
sub();
输出结果:
1 3 3 4 4
解析:
执行myObject.add()时,add里的this指向myObject,所以先将myObject中的num改为3;
接着执行下边的自执行函数,没有任何对象调用该函数,所以该自执行函数的 this 指向 window,所以输出 1,再将window中num改为4;
接着输出 myObject的 num,3;
输出 3;
输出 4;
var sub = myObject.sub 此时 sub只是一个函数指针,最后在window下调用这个函数,所以sub()的this指向 window,输出 4;
2.
var name = 'window'
var person1 = {
name: 'person1',
show1: function () {
console.log(this.name)
},
show2: () => console.log(this.name),
show3: function () {
return function () {
console.log(this.name)
}
},
show4: function () {
return () => console.log(this.name)
}
}
var person2 = { name: 'person2' }
person1.show1()
person1.show1.call(person2)
person1.show2()
person1.show2.call(person2)
person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()
person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()
输出结果:
person1.show1() // person1
person1.show1.call(person2) // person2
person1.show2() // window
person1.show2.call(person2) // window
person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window
person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2
其中最让人不解的应该是 show2 和 show4
解析:
person1.show2() 实际是执行:()=>console.log(this.a),根据“箭头函数的this指向外层作用域”,该箭头函数外层没有函数了,即指向全局作用域
这里很有迷惑性的地方是:容易把person1这个对象的{}大括号理解为函数作用域,实际上对象并没有生成作用域!!
person1.show2.call(person2)这个根据“箭头函数直接应用bind、call、apply不起作用”,很容易可知这里this依然指向全局
person1.show4()()实际是执行了以下两步:
执行:function () { return ... }返回一个箭头函数
return回来的这个箭头函数再执行
该箭头函数执行时它的外层作用域是这个show4的function,这个function的this指向person1,所以箭头函数中的this指向person1
很容易可得person1.show4().call(person2)指向person1
person1.show4.call(person2)()这个执行的时候,显式绑定将person2绑定到show4这个function上,那么依上面的分析,该箭头函数指向show4的function,所以箭头函数的this指向了person2
注意箭头函数:
箭头函数没有自己的this,它的this指向的是谁调用箭头函数的外层function,箭头函数的this就是指向谁,如果箭头函数没有外层函数,则指向window。
3.
var name = 'window'
function Person(name) {
this.name = name;
this.show1 = function () {
console.log(this.name)
}
this.show2 = () => console.log(this.name)
this.show3 = function () {
return function () {
console.log(this.name)
}
}
this.show4 = function () {
return () => console.log(this.name)
}
}
var personA = new Person('personA')
var personB = new Person('personB')
personA.show1()
personA.show1.call(personB)
personA.show2()
personA.show2.call(personB)
personA.show3()()
personA.show3().call(personB)
personA.show3.call(personB)()
personA.show4()()
personA.show4().call(personB)
personA.show4.call(personB)()
输出结果:
personA.show1() // personA
personA.show1.call(personB) // personB
personA.show2() // personA
personA.show2.call(personB) // personA
personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window
personA.show4()() // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB
这组例子和上一题的对比我有两个疑惑点:
- show2为什么会输出两次personA?不是指向外层作用域吗?这里外层作用域难道不是全局了?
- new绑定的优先级不是高于显示绑定吗?为什么personA.show1.call(personB)还能变成personB?
首先针对第一个疑惑点要说明的是:构造函数创建对象后其实多了一层构造函数的作用域,如图所示:
解析:
可以看到personA的scope和person1的差别:person1中的show1作用域仅仅是global,而personA中show1的函数作用域链是从构造函数产生的闭包开始的!
所以这样就很容易能知道show2的执行为什么会不同了:
personA的show2中的this指向构造函数形成的闭包函数,即Person函数,所以这里输出personA