JS中this的指向是谁?为什么在不同场景下this总是不同。这是学习JS中总会不解的难题。理解this
是掌握JS核心的关键之一。
this是什么?
除箭头函数中的this外,this是一个运行时确定的。它的指向取决于函数的调用方式,而不是如何声明的。也就是在执行上下文的创建阶段,JS引擎根据当时调用情况确定了this的指向。
打个比方讲执行上下文是一个会议本身 ,this是会议主席,谁是会议主席。取决于谁发起了这次会议。有如下几种情况:
- 老板发起的会议(老板就是对象),主席(this)就是老板
- 如果是在公共论坛上任何人发言(独立调用),主席就是论坛主持人(全局对象/undefined)要看论坛的环境(是否是严格模式)
- 如果是老板委托某人主持会议发言(call/apply),那么委托的这个人(call/apply指定的对象)就是主席(this)。
- 如果老板委托某人某段时间(应用存在的时间)内主持会议发言(bind),那么在某段时间内委托的人(bind指定的对象)就是主席(this)
this的指向
JS引擎在执行上下文创建阶段,会按照一套规则来分析,this的绑定谁。而这套规则是有一个优先级的判断。现在从高到低一一介绍。
1. new 绑定
new 绑定就是通过new操作符进行调用函数是,JS引擎认为这个函数是一个构造函数。此时有一系列操作:
- 创建一个空的新对象,对这个空对象的[[Prototype]]会指向函数的prototype对象
- 这个新对象将绑定到执行上下文AO的属性
ThisBinding
上 - 如果函数没有返回其他对象,通过
new
操作符调用的函数会自动返回这个新对象
js
function Person(name) {
this.name = name;
this.sayName = function() {
return `My name is ${this.name}`
}
// 隐式的返回了这个this
}
var p = new Person('lucy');
p.sayName() // `My name is lucy`
2. 显示绑定
通过函数中call
、apply
、bind
方法,强制指定函数执行时this
的指向。它们都能改变this的指向,但是调用方式或行为上有点略微的差别。
js
function calculatePrice(total) {
return total * this.price;
}
var product1 = {
price: 2,
};
var product2 = {
price: 3,
};
var product3 = {
price: 5,
};
console.log(calculatePrice.call(product1, 5)); // 10
console.log(calculatePrice.apply(product2, [10])); // 30
const newCalculatePrice = calculatePrice.bind(product3);
console.log(newCalculatePrice(15)); // 75
product3 = {
price: 6,
};
console.log(newCalculatePrice(15)); // 75
const newCalculatePrice2 = calculatePrice.bind(product3);
console.log(newCalculatePrice2(15)); // 90
3. 隐式绑定
当函数作为了对象的一个属性时,通过对象调用属性的方法时,this指向这个对象。
js
var name = 'jake'
var girl = {
name: "lucy",
age: 18,
sayHello: function () {
console.log("Hello, my name is " + this.name);
},
};
girl.sayHello(); // 'Hello, my name is lucy'
// 将sayHello赋值给了另一个变量的话,this就改变了
// 这是因为sayHello现在是独立调用。
var sayHello = girl.sayHello;
sayHello() // 'Hello, my name is jake'
4. 独立调用
当函数独立调用时,this的指向全局对象 。在严格模式下为undefined。
js
function showThis() {
console.log(this);
}
showThis() // 浏览器非严格模式下 window 严格模式下undefined node.js下为global
5. 其他情况
箭头函数
箭头函数没有自己的this
。当创建箭头函数的执行上下文时,直接获取外层执行上下文中的this
。就是说箭头函数不是在运行时所决定的,而是在定义它时已经确定了this
的指向。属于静态绑定。从性能上看的话,箭头函数要略优于普通函数
。
js
var obj = {
name: "object",
};
var name = "global object";
var sayName = () => {
console.log(this.name);
};
sayName.call(obj); // 'global object' 无法通过显示绑定修改this的指向
再来看一个例子:
js
var a = 0;
function Foo() {
this.a = 1;
var bar = () => {
console.log(this.a);
};
bar(); // 1 因为bar的执行上下文的this是直接获取Foo的执行上下文的this,而它的this就是一个对象,并且属性a值为1.
}
const f = new Foo();
DOM事件处理的函数
DOM事件绑定两种:
- 在HTML的onclick属性添加处理函数
html
<button onclick="click()">点击</button>
<script>
function click () {
console.log(this) // window对象
}
</script>
- 使用addEventListener方法注册事件处理函数
js
<button id="btn" >点击</button>
<script>
const btn = document.getElementById('#btn')
btn.addEventListener('click', function () { // 函数改为箭头函数的话 this指向window
console.log(this === btn) // true
})
</script>
小结
本章主要讲解一下内容:
- this指向的确定时机和确定规则优先级为: new > 显示绑定 > 隐式绑定 > 独立调用
- 箭头函数中的this的确定时机以及指向问题
- DOM事件处理函数this的表现