最终理解 JavaScript 中“This”的高级用法
在这个简短的教程中,我们将通过 7 个不同的示例学习如何在 Javascript 中使用“this”……
如果函数定义为箭头函数:{: #arrow-functions }
const arrowFunction = () => {
console.log(this);
};
在这种情况下,的值this
始终与父范围中的相同:this
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
箭头函数很棒,因为的内部值this
无法改变,它始终
与外部的值相同this
。
其他示例
使用箭头函数时,的值this
不能通过来改变bind
:
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
使用箭头函数时, 的值this
不能通过call
或apply
来改变:
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
this
使用箭头函数时,不能通过将函数作为另一个对象的成员来调用来更改的值
:
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
this
使用箭头函数时,不能通过将函数作为构造函数调用来更改的值
:
// TypeError: arrowFunction is not a constructor
new arrowFunction();
“绑定”实例方法
对于实例方法,如果要确保this
始终引用类实例,最好的
方法是使用箭头函数和类
字段:
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
当在组件(例如
React 组件或 Web 组件)中使用实例方法作为事件监听器时,此模式非常有用。
上述内容可能感觉像是违反了“与父范围内this
相同”规则,但如果您将类字段视为 在构造函数中 设置事物的语法糖,它就开始有意义了:this
class Whatever {
someMethod = (() => {
const outerThis = this;
return () => {
// Always logs `true`:
console.log(this === outerThis);
};
})();
}
// …is roughly equivalent to:
class Whatever {
constructor() {
const outerThis = this;
this.someMethod = () => {
// Always logs `true`:
console.log(this === outerThis);
};
}
}
替代模式包括在构造函数中绑定现有函数,或
在构造函数中分配函数。如果由于某些原因无法使用类字段,则在
构造函数中分配函数是一种合理的选择:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
否则,如果使用以下方式调用函数/类new
:{: #new }
new Whatever();
上述代码将调用Whatever
(如果它是一个类,则调用它的构造函数)并将 结果this
设置为。Object.create(Whatever.prototype)
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
对于旧式的构造函数也是如此:
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
其他示例
当使用 调用时new
, 的值this
不能用 更改bind
:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
当使用 调用时new
, 的值this
不能通过将函数作为
另一个对象的成员调用来更改:
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
否则,如果函数具有“绑定”this
值:{: #bound }
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
无论何时boundFunction
调用,其值将是传递给 ( )的this
对象。bind
boundObject
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
避免使用bind
将函数绑定到其外部函数this
。相反,使用箭头函数,因为它们this
从函数声明中就明确了,而不是
在代码后面发生的事情。
不要使用bind
set 来设置this
与父对象无关的值;这通常是意料之外的,也是它this
名声不好的原因。考虑将值作为参数传递;它更明确,并且适用于箭头函数。
其他示例
this
调用绑定函数时,不能使用call
或apply
更改的值:
// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
this
当调用绑定函数时,不能通过将该函数作为
另一个对象的成员调用来更改的值:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
否则,如果this
在调用时设置:{: #call-apply }
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
的值是传递给/ 的this
对象。call
apply
不要使用call
/apply
来设置this
与父对象无关的值;这通常是意料之外的,也是它this
名声不好的原因。考虑将值作为参数传递;这样更明确,并且适用于箭头函数。
不幸的是this
,它被 DOM 事件监听器之类的东西设置为其他值,使用它可能会
导致难以理解的代码:
element.addEventListener('click', function (event) {
// Logs `element`, since the DOM spec sets `this` to
// the element the handler is attached to.
console.log(this);
});
我避免this
在上述情况下使用,而是:
element.addEventListener('click', (event) => {
// Ideally, grab it from a parent scope:
console.log(element);
// But if you can't do that, get it from the event object:
console.log(event.currentTarget);
});
否则,如果通过父对象调用该函数(parent.func()
):{: #object-member }
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
在这种情况下,该函数作为 的成员函数被调用obj
,因此this
将是obj
。这发生在调用时,因此,如果在没有其父对象的情况下调用该函数,或者使用 不同的父对象,
则链接会断开:
const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);
const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
someMethod() === obj
为 false,因为someMethod
不是作为 的成员调用的obj
。你可能
在尝试如下操作时遇到过这个问题:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
这会中断,因为 的实现querySelector
会查看其自身的this
值,并期望
它是一个 DOM 节点,而上面的代码破坏了这种联系。正确实现上述代码的方法如下:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
有趣的事实:并非所有 API 都this
在内部使用。控制台方法(例如)console.log
已更改为
避免this
引用,因此log
无需绑定到console
。
不要仅仅为了设置this
与父对象无关的值而将函数移植到对象上;这通常是意料之外的,也是它this
名声不佳的原因。考虑将值作为参数传递;它更明确,并且适用于箭头函数。
否则,如果函数或父范围处于严格模式:{: #strict }
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
在这种情况下,的值为this
未定义。如果父范围处于严格 模式(并且所有模块都处于严格模式),则'use strict'
函数中不需要。
别太依赖这个。我的意思是,有更简单的方法可以获取undefined
值😀。
否则:{: #otherwise }
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
在这种情况下, 的值this
与 相同globalThis
。
大多数人(包括我)都称之为globalThis
全局对象,但这在技术上并非 100% 正确。Mathias Bynens 对此进行了详细说明(https://mathiasbynens.be/notes/globalthis#terminology),包括为什么这样称呼它,globalThis
而不是简单地这样称呼global
。
避免使用this
来引用全局对象(是的,我仍然这么称呼它)。相反,使用globalThis
,
这样更明确。
学习 Javascript 的其他资源:
获取书籍: 《JavaScript 挑战》
Monster JavaScript 课程 - 50 多个项目和应用程序
参考网站:https://web.dev/javascript-this/
鏂囩珷鏉yu簮锛�https://dev.to/bricourse/finally-understanding-the-advanced-uses-of-this-in-javascript-40g8