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

分析:

  1. 首先调用myObject.add(),函数内的this是指向myObject的,所以这里的this.num = 3实际是覆盖了myObject的num,此时add外层同级的num变成了3;接下来是自执行函数,自执行函数没有任何对象调用它则是指向全局,所以第一个console输出的是1,然后将全局的num改成了4;最后执行的this.num是输出3这个没什么难理解的了~
  2. myobject.num现在已经被修改成了3,因此第二个输出的是3
  3. 全局的num已经改成了4,因此第三个输出的是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()()实际是执行了以下两步:

  1. 执行:function () { return ... }返回一个箭头函数
  2. 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

这组例子和上一题的对比我有两个疑惑点:

  1. show2为什么会输出两次personA?不是指向外层作用域吗?这里外层作用域难道不是全局了?
  2. new绑定的优先级不是高于显示绑定吗?为什么personA.show1.call(personB)还能变成personB?

首先针对第一个疑惑点要说明的是:构造函数创建对象后其实多了一层构造函数的作用域,如图所示:

可以看到personA的scope和person1的差别:person1中的show1作用域仅仅是global,而personA中show1的函数作用域链是从构造函数产生的闭包开始的!

所以这样就很容易能知道show2的执行为什么会不同了:

personA的show2中的this指向构造函数形成的闭包函数,即Person函数,所以这里输出personA

 

第二个疑惑点