原文连接:A guide to this in JavaScript
原文做者:Ashay Mandwarya
译者:JintNiu
推荐理由:this
一直是JavaScript
中的重难点,借助这篇文章,从新认识并理解this
,加深印象。javascript
this
无疑是 JavaScript
中使用最普遍但又容易被误解的关键字,今天我将会对其进行详细的解释。html
当咱们在学校学习英语代词时:java
Phelps is swimming fast because he wants to win the race.数组
这句话中,咱们不直接使用 Phelps,而是使用代词“he”来指代他。相似地,JavaScript
中使用 this
关键字指向引用上下文中的对象。闭包
例:app
var car = {
make: "Lamborghini",
model: "Huracán",
fullName: function () {
console.log(this.make + " " + this.model);
console.log(car.make + " " + car.model);
}
}
car.fullName();
// Lamborghini Huracán
// Lamborghini Huracán
复制代码
在上面的代码中,咱们定义了一个具备属性 make
,model
和 fullName
的对象 car
,其中 fullName
是一个函数,函数体内使用 2 种不一样的方法输出 make
和 model
。ide
this
时,this.make+ " " +this.model
中的 this
指的是在上下文中的对象,也就是 car
,则 this.make
为 car.make
,this.model
为 car.model
;car.make
和 car.model
。如今咱们已经了解了什么是 this
以及它 最基本的用法,为方便记忆,咱们将列出一些场景,并分别举例说明。函数
根据出现的位置,this
可分为如下几种状况:post
call()
和 apply()
this
当 this
在方法内使用时,指向其所属的对象。学习
在对象内定义的函数称为方法。再来看看汽车的例子:
var car = {
make: "Lamborghini",
model: "Huracán",
fullName: function () {
console.log(this.make + " " + this.model);
console.log(car.make + " " + car.model);
}
}
car.fullName();
复制代码
该例中,fullName()
即为方法,方法中的 this
指向对象 car
。
this
函数中的 this
就有些复杂了。与对象同样,函数也具备属性。函数每次执行时都会获取 this
,它指向调用它的对象。
this
实际上只是“先行对象”的一种快捷引用,也就是对象调用。
— javascriptissexy.com
若是函数未被某对象调用,则函数内的 this
属于全局对象,该全局对象被称为 window
。在这种状况下,this
将指向全局做用域中的变量。且看如下例子:
var make = "Mclaren";
var model = "720s"
function fullName() {
console.log(this.make + " " + this.model);
}
var car = {
make: "Lamborghini",
model: "Huracán",
fullName: function () {
console.log(this.make + " " + this.model);
}
}
car.fullName(); // Lmborghini Huracán
window.fullName(); // Mclaren 720S
fullName(); // Mclaren 720S
复制代码
该例中,在全局对象中定义了 make
, model
和 fullName
,对象 car
中也实现了 fullName
方法。当使用 car
调用该方法时,this
指向该对象内的变量;而使用另外两种调用方式时,this
指向全局变量。
this
当单独使用 this
,不依附于任何函数或者对象时,指向全局对象。
这里的 this
指向全局变量 name
。
this
JS
中有不少种事件类型,但为了描述简单,这里咱们以点击事件为例。
每当单击按钮并触发一个事件时,能够调用另外一个函数来去执行某个任务。若是在函数内使用 this
,则指向触发事件中的元素。DOM 中,全部元素都以对象的形式储存,也就是说网页元素实际上就是 DOM 中的一个对象,所以每触发一个事件时,this
就会指向该元素。
例:
<button onclick="this.style.display='none'">
Remove Me!
</button>
复制代码
bind
:容许咱们在方法中设置 this
指向call
&apply
:容许咱们借助其余函数并在函数调用中改变 this
的指向。有关 call()
, apply()
和 bind()
的知识会在另外一篇文章阐述。
理解掌握了 this
会使工做变轻松不少,但实际状况每每不是那么如意。请看如下例子。
var car = {
make: "Lamborghini",
model: "Huracán",
name: null,
fullName: function () {
this.name = this.make + " " + this.model;
console.log(this.name);
}
}
var anotherCar = {
make: "Ferrari",
model: "Italia",
name: null
}
anotherCar.name = car.fullName();
// Lamborghini Huracán
复制代码
结果并非咱们所指望的。分析其缘由:当咱们使用 this
调用另外一个对象的方法时,只是为 anotherCar
分配了该方法,但实际调用者是 car
。所以返回的是 Lamborghini 而不是 Ferrari。
咱们可使用 call()
解决这个问题。
该例中利用 call()
方法使 anotherCar
对象调用 fullName()
,该对象中本来并无 fullName()
方法,但输出了 Ferrari Italia
。
另外,当咱们输出 car.name
和 anotherCar.name
的值时,前者输出 null
,然后者输出了 Ferrari Italia
,也就是说 fullName()
函数确实被 anotherCar
调用了,而不是被 car
调用。
var cars = [
{ make: "Mclaren", model: "720s" },
{ make: "Ferrari", model: "Italia" }
]
var car = {
cars: [{ make: "Lamborghini", model: "Huracán" }],
fullName: function () {
console.log(this.cars[0].make + " " + this.cars[0].model);
}
}
var vehicle = car.fullName;
vehicle() // Mclaren 720s
复制代码
该例中,咱们定义了一个全局变量 cars
,而且在对象 car
中也定义了同名变量,接着将 fullName()
方法赋给变量 vehicle
,而后调用它。该变量属于全局变量,因为上下文的关系,this
指向的是全局变量 cars
而不是局部变量。
咱们可使用 bind()
解决这个问题。
bind
改变了this
的指向,使变量 vehicle
指向局部变量 car
。也就是说,this
的指向取决于 car
的上下文环境。
var car = {
cars: [
{ make: "Lamborghini", model: "Huracán" },
{ make: "Mclaren", model: "720s" },
{ make: "Ferrari", model: "Italia" }
],
brand:"lamborghini",
fullName: function () {
this.cars.forEach(function(car){
console.log(car.model + " " + this.brand);
})
}
}
car.fullName();
// Huracán undefined
// 720s undefined
// Italia undefined
复制代码
在以上代码中,fullName()
使用 forEach
迭代数组 cars
,每次迭代都产生一个没有上下文的匿名函数,这类定义在函数内部的函数,称之为闭包(closure
)。闭包在 JavaScript
中很是重要,并且被普遍使用。
另外一个重要的概念是做用域(scope
)。定义在函数内部的变量不能访问其做用域之外的变量和属性;匿名函数中的 this
不能访问外部做用域,以致于 this
只能指向全局对象。该例中,全局对象中没有定义 this
所要访问的属性 brand
,所以输出 undefined
。
以上问题的解决方法是:咱们能够在匿名函数外为 this
赋值,而后在函数内使用。
将 this
赋给变量 self
,并代替函数体内的 this
,输出指望结果。
var car = {
make: "Lamborghini",
model: "Huracán",
fullName: function (cars) {
cars.forEach(function (vehicle) {
console.log(vehicle + " " + this.model);
})
}
}
car.fullName(['lambo', 'ferrari', 'porsche']);
// lambo undefined
// ferrari undefined
// porsche undefined
复制代码
当没法使用 this
进行访问时,可使用变量 self
来保存它(如例 3 ),但在该例中,也可使用箭头函数来解决:
能够看出,在 forEach()
中使用箭头函数就能够解决该问题,而不是进行绑定或暂存 this
。这是因为箭头函数绑定了上下文,this
实际上指向原始上下文或原始对象。
var car = {
make: "Lamborghini",
model: "Huracán",
fullName: function () {
console.log(this.make + " " + this.model);
}
}
var truck = {
make: "Tesla",
model: "Truck",
fullName: function (callback) {
console.log(this.make + " " + this.model);
callback();
}
}
truck.fullName(car.fullName);
// Tesla Truck
// undefined undefined
复制代码
上述代码中定义了两个相同的对象,但其中一个包含回调函数,回调函数将做为参数传入另外一个函数,而后经过外部函数调用来完成某种操做。
该代码中对象 truck
的 fullName
方法包含一个回调函数,并在方法中直接进行调用。当将 car.fullName
做为参数调用 truck.fullName()
时,输出 Tesla Truck
和 undefined undefined
。
结果出乎意料。实际上,car.fullName
只是做为参数传入,而不是由 truck
对象调用。换句话说,回调函数调用了对象 car
的方法,但却把 this
绑定到全局做用域上,以下图:
为便于观察,咱们输出了 this
。能够看到回调函数中的 this
指向了全局做用域。继续建立全局变量 make
和 model
以下例:
显而易见,回调函数中的输出了全局变量 make
和 model
,再次证实了 this
指向全局对象。
为获得指望结果,咱们将使用 bind()
将 car
强制绑定到回调函数中。以下:
毫无疑问,this
是很是有用的,但不容易理解。但愿经过这篇文章你能够逐渐了解它的使用方法。
若是这篇文章对您有所帮助,点个赞👏,加个关注👣 吧~