该this
关键字可以是非常混乱。本教程将帮助您了解其工作原理。您将了解如何this
在不同的上下文和环境下工作。这些上下文包括全局对象,函数,对象和类的方法和事件。您还将了解globalThis
ES2020中添加的新功能。
介绍
该this
是存在不只是在JavaScript中,而且在其他编程语言的特殊关键字。JavaScript的this
不同之处在于在不同模式下的行为不同。在JavaScript中,有两种模式:严格和非严格。非严格模式是普通模式。有时也称为“马虎”模式。
什么this
是指总是依赖于它定义的执行上下文。执行上下文是当前环境或范围,在其中声明正在执行的代码行。在运行时,JavaScript会维护所有执行上下文的堆栈。
该堆栈顶部的执行上下文是正在执行的上下文。当执行上下文更改时,更改的值也将this
更改。让我们看一下this
在不同上下文中指的是什么。
请注意严格模式
严格模式旨在帮助您使JavaScript代码更简洁。它通过建立一些特殊规则来做到这一点。例如,必须先明确声明所有变量,然后才能为其分配值。您的函数必须在全局范围内声明。在函数内部,禁止对变量和函数参数使用相同的名称。
也禁止删除不可变的属性和不合格的标识符。所有这些以及许多其他事情都会引发错误。这是严格和非严格模式之间的另一个区别。在非严格模式下,许多错误是静默的。严格来说,它们不是。违反严格模式规则的所有内容都将引发错误。
除了这些规则外,严格模式还改变了this
行为方式。为了更好的说明,我们将讨论如何在每个特定的上下文中。关于严格模式的最后一件事。如果要切换到严格模式'use strict';
,请在代码顶部添加语句。
注意:您可以为所有代码或仅为特定功能打开严格模式。区别在于您在哪里使用该'use strict';
语句。在全局范围的顶部使用它,它将应用于随后的所有代码。在函数顶部使用它,它将仅应用于该函数内部的代码。
全球范围内的“这个”
在JavaScript中,当this
在全局上下文中定义时this
,默认情况下指的是Global对象。对于浏览器,此全局对象是window
对象。此全局对象是顶级范围。对于this
全局上下文,严格模式没有任何区别。无论您处于严格模式还是非严格模式,this
行为都将相同。
// global context and this in non-strict mode
console.log(this === window)
// true
// global context and this in strict mode
'use strict'
console.log(this === window)
// true
“ this”,全局上下文和Node.js
对于Node.js,没有window
对象。在Node.js中,全局对象是一个特殊的对象,称为global。这意味着在全球范围内this
将引用this global
。好吧,差不多。这仅在Node.js内部是正确的。要对此进行测试,请首先启动您喜欢的控制台并输入node
。
此命令将打开Node.js环境,因此您可以直接使用它。之后,您可以测试this
全局上下文中的引用。如果您的计算机上没有Node.js,则可以从Node.js 网站获取。
// In node environment
> console.log(this === global)
// true
> console.log(this)
// Object [global] {
// global: [Circular],
// clearInterval: [Function: clearInterval],
// clearTimeout: [Function: clearTimeout],
// setInterval: [Function: setInterval],
// setTimeout: [Function: setTimeout] { [Symbol(util.promisify.custom)]: [Function] },
// queueMicrotask: [Function: queueMicrotask],
// clearImmediate: [Function: clearImmediate],
// setImmediate: [Function: setImmediate] {
// [Symbol(util.promisify.custom)]: [Function]
// }
// }
如果从JavaScript文件运行代码,结果将有所不同。当您在Node.js中使用JavaScript文件时,本地代码仅限于该文件。那里的一切都不是全球性的,而是本地性的。结果,this
不是指global
,而是指module.exports
。
// In node environment, in JavaScript file
console.log(this === global)
// false
console.log(this === module.exports)
// true
功能和“ this”
对于JavaScript顶级功能,模式很重要。顶层是指在全局范围内声明的函数,而不是在对象或类内部声明的函数。如果您在非严格模式下工作,this
则window
在浏览器的情况下将引用全局对象。
// Function in a non-strict mode
function testThis() {
console.log(this === window)
}
testThis()
// true
让我们use strict
在函数顶部添加语句以打开严格模式。现在,结果将有所不同。this
将不再引用全局对象,例如window
。当您尝试获取this
JavaScript 的值时将返回undefined
。这是因为this
现在未设置的值。
// Function in a non-strict mode
function testThis() {
'use strict' // switch on strict mode for this function
console.log(this === window)
console.log(this)
}
testThis()
// false
// undefined
函数,this和call()和apply()
有一种方法可以设置this
您在调用函数时的值,而不必设置它undefined
。为此,您可以使用call(),apply()或bind()方法。这称为“显式函数绑定”。当您使用这些方法之一时,您将this
作为参数传递值。前两个call()
和apply()
几乎相同。
区别在于apply()
接受参数列表与call()
接受参数数组相同。apply()
还允许您使用数组文字。
// Set value of this with apply()
function testThisWithApply() {
'use strict'
console.log('Value of this: ', this)
}
// set value of "this" to one
testThis.apply('one')
// 'Value of this: one'
// Set value of this with call()
function testThisWithCall() {
'use strict'
console.log('Value of this: ', this)
}
// set value of "this" to one
testThis.call('one')
// 'Value of this: one'
函数,this和bind()
的bind()
方法是不同的。当您要调用或调用一个函数时,不要使用此方法。而是使用该bind()
方法来创建新的“绑定”函数。之后,您将调用新的“绑定”函数,而不是原始函数。现在,价值this
就是您想要的价值。
// Set value of this with bind()
function testThisWithBind() {
'use strict'
console.log('Value of this: ', this)
}
// Create bound function and set value of "this" to "one"
const newTestThisWithBind = testThisWithBind.bind('one')
// Invoke new "bound" function "newTestThisWithBind"
newTestThisWithBind()
// 'Value of this: one'
// Or, with reassigning the original function
function testThisWithBind() {
'use strict'
console.log('Value of this: ', this)
}
// Create bound function and set value of "this" to "reassigned!"
testThisWithBind = testThisWithBind.bind('reassigned!')
// Test: Invoke now "bound" function "testThisWithBind"
testThisWithBind()
// 'Value of this: reassigned!'
关于bind()
要记住的方法,有一件重要的事情。它只能工作一次。您不能bind()
多次更改this
“ bound”函数的值。但是,您可以将其与原始函数一起使用多次,以创建新的“绑定”函数。
// Doesn't work: Try to re-set this of bound function
function testThisWithBind() {
'use strict'
console.log('Value of this: ', this)
}
// Create bound function
// and set value of "this" to "one"
const newTestThisWithBind = testThisWithBind.bind('one')
// Test: Invoke new "bound" function "newTestThisWithBind"
newTestThisWithBind()
// The value of "this" is not correct
// 'Value of this: one'
// Create another bound function
// using the bound function
// and try to change value of "this"
const newerTestThisWithBind = newTestThisWithBind.bind('two')
// Test: Invoke newer "bound" function "newerTestThisWithBind"
newerTestThisWithBind()
// The value of "this" is correct
// 'Value of this: one'
// Works: Create another bound function from the original
const brandNewThisWithBind = testThisWithBind.bind('two')
// Test: Invoke newer "bound" function "brandNewThisWithBind"
brandNewThisWithBind()
// The value of "this" is correct
// 'Value of this: two'
// Test: Invoke newer "bound" function "newerTestThisWithBind"
// The value of "this" is the same
newerTestThisWithBind()
// 'Value of this: one'
注意:这适用于熟悉React和类组件的人员。你可能会认识到喜欢的东西this.myFunc = this.myFunc.bind(this)
在constructor
。这是因为它需要一个函数并创建一个绑定函数并返回它,并且基本上覆盖了原始函数。
在这种情况下,this
here 的值是this
,即类组件本身。this
在这种情况下,更改绑定的另一种方法是使用箭头功能。
箭头功能和“ this”
ES6中引入的箭头功能与常规功能不同。箭头函数没有自己的this
。它们始终使用与this
其父级相同的值来声明它们的执行上下文。箭头函数的另一重要事项是您不能this
显式设置其值。
当您尝试使用call()
,apply()
或bind()
用箭头功能什么都不会发生。箭头函数将忽略这些方法。
// Arrow function inside an object
const user = {
username: 'franky',
email: 'franky@frankyrocks.io',
// Get data with arrow function
getUserWithArrowFunction: () => {
// This refers to global object, window
// So, this.username is like window.username
return `${this.username}, ${this.email}.`
},
// Get data with normal function
getUserWithNormalFunction: function() {
// This refers to myObj
// So, this.username is like myObj.username
return `${this.username}, ${this.email}.`
}
}
// Test the arrow function
user.getUserWithArrowFunction()
// TypeError: Cannot read property 'title' of undefined
// Test the normal function
user.getUserWithNormalFunction()
// 'franky, franky@frankyrocks.io.'
///
// Arrow functions and binding
let arrowFunctionWithBind = () => {
'use strict'
console.log('Value of this: ', this)
}
// Try to create bound function
// and set value of "this" to "arrow!"
arrowFunctionWithBind = arrowFunctionWithBind.bind('arrow!')
// Test: Invoke new "bound" function "arrowFunctionWithBind"
arrowFunctionWithBind()
// 'Value of this: undefined
由于this
箭头函数的工作方式,箭头函数是回调的理想选择。请记住,箭头函数始终this
从其封闭的执行上下文继承。使用箭头功能,可以this
在回调中进行访问而不必担心是什么this
。
// Functions as callbacks
// Using normal function as a callback
const counter = {
count: 0,
addCount() {
// Use normal function as a callback in setInterval
setInterval(function() {
// 'this' here is Global object
// So, ++this.count is like ++window.count
console.log(++this.count)
}, 1000)
}
}
// Invoke addCount() method
counter.addCount()
// NaN
// NaN
// NaN
// NaN
// NaN
// ...
// Using arrow function as a callback
const counter = {
count: 0,
addCount() {
// Use arrow function as a callback in setInterval
setInterval(() => {
// 'this' here is the "counter" object
// So, ++this.count is like ++counter.count
console.log(++this.count)
}, 1000)
}
}
// Invoke addCount() method
counter.addCount()
// 1
// 2
// 3
// 4
// 5
// ...
///
// What "this" is
// Using normal function as a callback
const counter = {
logThis() {
// Use normal function as a callback in setInterval
setInterval(function() {
console.log(this)
}, 1000)
}
}
// Invoke logThis() method
counter.logThis()
// Window
// Window
// Window
// ...
// What "this" is
// Using arrow function as a callback
const counter = {
logThis() {
// Use normal function as a callback in setInterval
setInterval(() => {
console.log(this)
}, 1000)
}
}
// Invoke logThis() method
counter.logThis()
// { logThis: [Function: logThis] }
// { logThis: [Function: logThis] }
// { logThis: [Function: logThis] }
// ...
对象方法和“ this”
假设您this
在对象内部的函数内部使用。在这种情况下,的值this
将是在其中声明该方法的对象。这与JavaScript模式无关。
// Create object
const animal = {
name: 'Cat',
class: 'Mammalia',
order: 'Carnivora',
genus: 'Felis',
logAnimal: function() {
return this;
}
}
// Call logAnimal() method
animal.logAnimal()
// {
// name: 'Cat',
// class: 'Mammalia',
// order: 'Carnivora',
// genus: 'Felis',
// logAnimal: [Function: logAnimal]
// }
声明函数在对象内部还是外部并附加它都没有关系。
// Create empty object
const thing = {}
// Add property to "thing" object
thing.itemName = 'Box'
// Add method to "thing" object
thing.getItemName = function() {
return this.itemName
}
thing.returnThis = function() {
return this
}
// Invoke getItemName() method
thing.getItemName()
// 'Box'
thing.returnThis()
// {
// itemName: 'Box',
// getItemName: [Function],
// returnThis: [Function]
// }
函数构造函数和“ this”
this
在函数构造函数中使用时,其值将始终引用该构造函数创建的新对象。
// Create function constructor
function Phone(model, brand) {
this.model = model
this.brand = brand
this.getModelAndBrand = function() {
// "this" refers to new Phone object
// created using "new" keyword
return `Model: ${this.model}, brand: ${this.brand}`
}
this.returnThis = function() {
return this
}
}
// Create new Phone object using "new" keyword
const iPhoneX = new Phone('iPhone X', 'Apple')
// Here, "this" refers to "iPhoneX"
iPhoneX.getModelAndBrand()
// 'Model: iPhone X, brand: Apple'
iPhoneX.returnThis()
// Phone {
// model: 'iPhone X',
// brand: 'Apple',
// getModelAndBrand: [Function],
// returnThis: [Function]
// }
类方法和“ this”
this
在类方法中使用时,它将引用用该类创建的实例。
// Create new class with two properties
// add two methods
class Brain {
constructor(numOfHemispheres, iq) {
this.numOfHemispheres = numOfHemispheres
this.iq = iq
}
getIQ() {
// This refers to instance of Brain class
return this.iq
}
learn() {
// This refers to instance of Brain class
this.iq += 1
}
watchTv() {
// This refers to instance of Brain class
this.iq -= 1
}
returnThis() {
return this
}
}
// Create instance of Brain class
// with 2 hemispheres and IQ of 180
const smartBrain = new Brain(2, 180)
// Log the IQ of smartBrain
smartBrain.getIQ()
// 180
// Learn something
smartBrain.learn()
// Log the IQ of smartBrain again
smartBrain.getIQ()
// 181
smartBrain.watchTv()
// Log the IQ of smartBrain again
smartBrain.getIQ()
// 180
smartBrain.returnThis()
// Brain { numOfHemispheres: 2, iq: 180 }
事件和“此”
当您使用this
内部事件处理程序时,它将引用您将事件侦听器附加到的元素。
创建一个简单的button
元素。
<!-- Create button -->
<button class="btn">Click</button>
附加eventListener
到button
元素。
// Create event handler function
handleButtonClick function() {
console.log(this)
}
// Find the button in the DOM,
// attach event listener to it
// and pass the handler function as an argument
document.querySelector('.btn').addEventListener('click', handleButtonClick)
现在单击按钮时,您将看到[object HTMLButtonElement]
和很多数据。这是按钮元素及其所有属性和方法。
事件,“ this”和箭头功能
如果将箭头函数用作事件处理程序的回调,则会得到不同的结果。这次,您将不会获得[object HTMLButtonElement]
,及其属性和方法。相反,您将获得[object Window]
全局window
对象。因此,如果要用于this
访问触发事件的元素,请使用常规功能。
如果无论出于何种原因仍要使用箭头功能,则有一种方法。将event
作为参数添加到箭头函数。然后,里面那个箭头功能,用途event.target
,event.currentTarget
来访问该元素。如果是按钮,您将得到[object HTMLButtonElement]
。
// Create handler function, now arrow function
// and specify parameter for event
const handleButtonClick = (event) => {
// Access the value passed as event, not "this"
console.log(event)
}
// Find the button in the DOM,
// attach event listener to it
// and pass the handler function as an argument
document.querySelector('.btn').addEventListener('click', handleButtonClick)
全球本
这globalThis
是ES2020中添加的功能之一。此功能旨在与全球合作this
。也就是说,用window
,self
,this
或frame
在浏览器和对象global
或this
在Node.js的 如果使用跨平台JavaScript,则不再需要担心使用正确的对象。
相反,您可以使用新添加的globalThis
。使用globalThis
,无论平台如何,您都将始终自动选择正确的全局对象。也就是说,globalThis
不应该被滥用。您仍应将尽可能多的代码保留在全局范围,函数和代码块之内。
本globalThis
应主要用于东西,如polyfills和垫片。globalThis
还可用于功能检测,以检测特定浏览器或环境中支持哪些JavaScript功能。
// In the browser
globalThis === window
// true
const obj = {
name: 'foo',
getThis: function() {
return this
},
getGlobalThis = function() {
return globalThis
}
}
obj.getThis()
// {name: "foo", getThis: ƒ}
obj.getGlobalThis()
// Window { ... }
// In Node.js
globalThis === global
// true
结论:JavaScript中的“ this”如何工作
您刚刚到达了本教程的结尾。我希望你喜欢它。我也希望它能帮助您了解this
关键字的工作原理和使用方法。该this
关键字可以非常混乱,可能需要一些时间去了解它。但是,这是值得的。了解了它之后,您还将更好地了解JavaScript本身。