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(),函数内的this是指向myObject的,所以这里的this.num = 3实际是覆盖了myObject的num,此时add外层同级的num变成了3;接下来是自执行函数,自执行函数没有任何对象调用它则是指向全局,所以第一个console输出的是1,然后将全局的num改成了4;最后执行的this.num是输出3这个没什么难理解的了~
- myobject.num现在已经被修改成了3,因此第二个输出的是3
- 全局的num已经改成了4,因此第三个输出的是4
- sub = myObject.sub这句将myObject.sub函数赋值给sub,而实际上造成了this的丢失,这里sub()执行时this是指向全局,输出的是全局的num,即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。
下面看另一道题:
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
第二个疑惑点