# 1. 谈谈对this的理解

参考答案:

this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。
在实际开发中,this 的指向可以通过四种调用模式来判断。
  • 1.第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。

  • 2.第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。

  • 3.第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。

  • 4.第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。

这四种方式,使用构造器调用模式的优先级最高,然后是 apply、call和 bind调用模式,
然后是方法调用模式,然后是函数调用模式。

  • 面试题一:
var name = 'window'
var person = {
    name: 'person',
    sayName: function () {
        console.log(this.name)
    }
}
function sayName () {
    var sss = person.sayName;
    sss();// window  独立调用指向全局对象window
    person.sayName(); // person  隐式调用
    (person.sayName)(); // person  和上一句效果一样(person.sayName) 带小括号不带小括号没区别
    (b = person.sayName)() // window  间接引用 ,(b = person.sayName)的结果是一个值,并没有和person对象有联系,所以后面加一个()进行调用的时候,还是属于默认绑定(直接调用),this指向全局对象
}
sayName();
  • 面试题二:
var name = 'window';
var person1 = {
    name: 'person1',
    foo1: function () {
        console.log(this.name)
    },
    foo2: () => {
        console.log(this.name)
    },
    foo3: function () {
        return function () {
            console.log(this.name)
        }
    },
    foo4: function () {
        return () => {
            console.log(this.name)
        }
    }
}

var person2 = { name: 'person2' }
// 正题来咯
person1.foo1(); // person1 (隐式绑定)
person1.foo1.call(person2); // person2 (call显示绑定person2,显示绑定优先级大于隐式绑定)

person1.foo2(); // window (箭头函数不绑定作用域,找到上层作用域为全局window)
person1.foo2.call(person2);// window (箭头函数没有this,call()无效)

person1.foo3()(); // window (person1.foo3()得到一个return的函数,再加上一个小括号属于独立调用指向全局对象window)
person1.foo3.call(person2)(); // window (person1.foo3.call(person2)这句话得到返回值后,后面加小括号调用,还是属于独立调用 指向window) 
person1.foo3().call(person2);// person2 (最终调用return函数时,显示绑定上了person2)

person1.foo4()();// person1 (person1.foo4()这句话得到return后的函数为箭头函数进行调用,箭头函数中没有this向上一层作用域找,上层作用域为foo4,this指向person1)
person1.foo4.call(person2)();// person2 (上层作用域被显示绑定为person2 ,在箭头函数中找不到this后,向上层所用域找 就找到了之前被显示绑定的person2)
person1.foo4().call(person2);// person1 (person1.foo4()这句话得到return后的函数为箭头函数进行显示绑定person2,但箭头函数中没有this,显示绑定无效,向上一层作用域找,找到函数foo4,this指向person1)
  • 面试题三:
var name = 'window'

function Person (name) {
    this.name = name
    this.foo1 = function () {
        console.log(this.name)
    }

    this.foo2 = () => console.log(this.name)

    this.foo3 = function () {
        return function () {
            console.log(this.name)
        }
    }

    this.foo4 = function () {
        return () => { console.log(this.name) }
    }
}

var person1 = new Person('person1')
var person2 = new Person('person2')


person1.foo1() // person1  (隐式绑定)
person1.foo1.call(person2) // person2 (显示绑定person2,显示绑定优先级高于person1.foo1的隐式绑定)

person1.foo2() // person1  (上层作用域中的this是person1函数)
person1.foo2.call(person2) // person1 (显示绑定在箭头函数中无效,所以结果还是上层作用域中的this是person1函数)

person1.foo3()() // window (person1.foo3()得到返回函数 加小括号进行直接调用 this指向全局作用域window)
person1.foo3.call(person2)() // window (person1.foo3.call(person2)得到返回函数后,独立函数调用指向全局)
person1.foo3().call(person2) // person2 (person1.foo3()得到返回函数后,显示绑定person2)

person1.foo4()() // person1 (箭头函数中没有this,向上一层作用域找 this指向person1)
person1.foo4.call(person2)() // person2 (person1.foo4.call(person2)显示绑定person2,得到返回的箭头函数后,向上一层作用域找,this指向person2)
person1.foo4().call(person2) // person1 (person1.foo4()得到返回箭头函数显示绑定person2无效,向上一层作用域找,this指向person1)