this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时。this的绑定与函数声明的位置无关,只取决于函数的调用方式。
this的四种调用模式:
- 普通函数调用模式
- 对象方法调用模式
- 构造器调用模式
- call()、apply()、bind()
1.普通函数调用模式
函数调用模式中的函数不是对象的属性值,而是直接作为函数调用,在这种模式下this指向全局对象。
function test() {
this.a = 3
}
test() //直接调用
console.log(a); // 3
如果上述代码运行在浏览器上,则a会被挂载到全局对象window上;如果运行在node环境下,则会挂载到全局对象global上。
2.对象方法调用模式
方法调用模式是指函数作为对象的的属性(方法)被调用。
const obj = {
name: 'lisi',
age: 13,
sayName: sayName
}
function sayName() {
console.log(this.name);
}
obj.sayName() //lisi
//添加额外代码
const obj2 = {
name: 'zs',
age: 12,
sayName: obj.sayName
}
obj2.sayName() //zs
上述代码中,obj.sayName()执行时,由于是obj调用sayName,所以this指向obj,结果打印lisi;obj2.sayName()也是同样的道理,但是obj2中有个迷惑性的地方obj2的sayName属性值是obj.sayName,obj.sayName只是函数的内存地址,并不是调用。总结而言,作为对象方法调用时,只需要看方法的调用者是谁就行了。
3.构造器调用模式
function Person(name) {
this.name = name
}
const p1 = new Person('lisi')
console.log(p1.name); //lisi
通过构造函数创建一个实例后,那么this就会指向这个实例。当然其结果是因为new操作时会自动执行一些操作。
- 在内存中创建一个新对象
- 将新对象的原型指向构造函数的prototype的值
- 将新对象赋值给构造函数内部的this(this指向新对象)
- 执行构造函数内部代码(给新对象添加属性和方法)
- 如果构造函数返回非空对象,则返回该对象;否则返回刚创建的新对象
我们可以看出,上述操作中有this指向这个新对象这一步,这就是this指向这个实例的原因。
4.call、apply、bind
使用call、apply、bind可以显示地将this指向某个对象。
//运行在浏览器上
var name = 'zs'
function sayName() {
console.log(this.name);
}
const obj = {
name: 'lisi'
}
sayName() //zs
function test() {
sayName.call(obj)
}
test() //lisi
以上代码中直接执行sayName时打印的是zs,但是通过test函数sayName.call()执行后,打印的是lisi。这说明了,第一次打印时,this指向的是全局;而第二次打印时,this指向的是obj。call的作用就是显示的更改了this的指向,同理apply与bind也是,而且使用方法大体相同。
apply与call的区别(面试中比较常见)
apply与call第一个参数都是this将指向的对象;apply第二个参数是一个数组作为参数,call第一个参数后可以接收任意个参数。
5.几种调用模式的优先级
构造器调用 > call、apply、bind > 对象方法调用 > 普通方法调用