“this”关键字如何工作,以及何时应该使用?
- 2024-11-02 21:00:00
- admin 原创
- 34
问题描述:
我正在寻找“this”关键字的作用以及如何正确使用它的清晰解释。
它的行为似乎很奇怪,我不完全明白为什么。
它如何this
工作以及何时使用?
解决方案 1:
this
是 JavaScript 中的一个关键字,是执行上下文的属性。它的主要用途是在函数和构造函数中。它的规则this
非常简单(如果你遵循最佳实践)。
this
规范中的技术描述
ECMAScript 标准通过this
抽象操作(缩写为AO)定义ResolveThisBinding:
[AO] ResolveThisBinding […] 使用正在运行的执行上下文
this
的 LexicalEnvironment确定关键字的绑定。[步骤]:
让envRec成为GetThisEnvironment ()。
返回?envRec.GetThisBinding ()。
全局环境记录、模块环境记录和函数环境记录各自都有自己的 GetThisBinding 方法。
GetThisEnvironment AO 查找当前正在运行的执行上下文的 LexicalEnvironment,并查找最近的具有 this 绑定(即 HasThisBinding 返回 true)的上升环境记录(通过迭代访问它们的 [[OuterEnv]] 属性)。此过程以三种环境记录类型之一结束。
常常的值this
取决于代码是否处于严格模式。
this
GetThisBinding 的返回值反映了当前执行上下文的值,因此每当建立新的执行上下文时,this
都会解析为一个不同的值。当修改当前执行上下文时也会发生这种情况。以下小节列出了可能发生这种情况的五种情况。
您可以将代码示例放入AST 资源管理器中,以便跟踪规范详细信息。
1. 脚本中的全局执行上下文
这是在顶层评估的脚本代码,例如直接在内部<script>
:
<script>
// Global context
console.log(this); // Logs global object.
setTimeout(function(){
console.log("Not global context");
});
</script>
当处于脚本的初始全局执行上下文中时,评估this
会导致GetThisBinding采取以下步骤:
全局环境记录envRec的 GetThisBinding 具体方法[…] [执行以下操作]:
返回envRec .[[GlobalThisValue]].
全局环境记录的 [[GlobalThisValue]] 属性始终设置为主机定义的全局对象,可通过以下方式访问globalThis
(window
在 Web 上、global
在 Node.js 上;在 MDN 上查看文档)。请按照InitializeHostDefinedRealm的步骤了解 [[GlobalThisValue]] 属性是如何产生的。
2.模块中的全局执行上下文
ECMAScript 2015 中引入了模块。
这适用于模块,例如直接在 内部时<script type="module">
,而不是简单的<script>
。
当处于模块的初始全局执行上下文中时,评估this
会导致GetThisBinding采取以下步骤:
模块环境记录的 GetThisBinding 具体方法 […] [执行以下操作]:
返回undefined。
在模块中, 的值this
始终undefined
位于全局上下文中。模块隐式处于严格模式。
3. 输入评估代码
有两种类型的eval
调用:直接调用和间接调用。这种区别从 ECMAScript 第 5 版开始就存在了。
直接
eval
调用通常看起来像eval(
...);
或(eval)(
...);
(或((eval))(
...);
等)。1只有当调用表达式符合狭窄模式时,它才是直接的。2间接调用涉及以任何其他方式
eval
调用函数引用。它可能是... ,... ... ,... ...等。给定,它也可能为... ,... 。另外,给定,调用...也是间接的。eval
`eval?.()
(, eval)(
)window.eval(
)eval.call(
,)
const aliasEval1 = eval; window.aliasEval2 = eval;aliasEval1(
)aliasEval2(
)const originalEval = eval; window.eval = (x) => originalEval(x);
eval(`)
请参阅chuckj 对“JavaScript 中的 (1, eval)('this') 与 eval('this')?”的回答,以及Dmitry Soshnikov 的 ECMA-262-5 详细信息 – 第 2 章:严格模式(已存档),了解何时可以使用间接eval()
调用。
PerformEval执行eval
代码。它创建一个新的声明性环境记录作为其词法环境,GetThisEnvironment 从中获取值this
。
然后,如果this
出现在代码中,则调用GetThisEnvironmenteval
找到的环境记录的 GetThisBinding 方法并返回其值。
并且创建的声明性环境记录取决于调用eval
是直接的还是间接的:
在直接评估中,它将基于当前正在运行的执行上下文的词法环境。
在间接评估中,它将基于执行间接评估的Realm Record的 [[GlobalEnv]] 属性(全局环境记录) 。
这意味着:
在直接评估中,
this
值不会改变;它是从调用的词法范围中获取的eval
。在间接求值中,
this
值是全局对象 (globalThis
)。
那new Function
? — new Function
与 类似eval
,但它不会立即调用代码;它会创建一个函数。this绑定在这里不适用于任何地方,除非调用函数,这将正常工作,如下一小节所述。
4. 输入功能代码
调用函数时输入函数代码。
调用函数的语法有四类。
针对以下三个执行 EvaluateCall AO :3
普通函数调用
可选的链式调用
标记模板
并针对此执行EvaluateNew :3
构造函数调用
实际函数调用发生在Call AO 中,它使用从上下文确定的thisValue进行调用;此参数在与调用相关的长链中传递。Call调用函数的[[Call]]内部槽。这将调用PrepareForOrdinaryCall,在其中创建一个新的函数环境记录:
函数环境记录是一种声明性环境记录,用于表示函数的顶级范围,如果函数不是 ArrowFunction ,则提供
this
绑定。如果函数不是ArrowFunction函数并且引用super
,则其函数环境记录还包含用于super
在函数内部执行方法调用的状态。
另外,函数环境记录中有一个[[ThisValue]]字段:
这是
this
用于本次函数调用的值。
NewFunctionEnvironment调用还设置函数环境的 [[ThisBindingStatus]] 属性。
[[Call]]还调用OrdinaryCallBindThis,其中适当的thisArgument根据以下内容确定:
原始参考,
函数的类型,以及
代码是否处于严格模式。
一旦确定,最后调用新创建的函数环境记录的BindThisValue方法实际上将 [[ThisValue]] 字段设置为thisArgument。
最后,这个字段是函数环境记录的 GetThisBinding AO 获取其值的地方this
:
函数环境记录envRec的 GetThisBinding 具体方法[…] [执行以下操作]:
[…]
返回envRec .[[ThisValue]].
再次强调,这个值究竟如何确定取决于许多因素;这只是一个总体概述。有了这些技术背景,让我们来研究一下所有具体的例子。
箭头函数
当评估箭头函数时,函数对象的 [[ThisMode]] 内部槽在OrdinaryFunctionCreate中设置为“lexical”。
在OrdinaryCallBindThis中,它采用函数F:
令thisMode为F .[[ThisMode]].
如果thisMode是词汇的,则返回 NormalCompletion(
undefined
)。[…]
这只是意味着跳过了绑定this的其余算法。箭头函数不绑定其自己的this值。
那么,this
箭头函数里面是什么呢?回顾ResolveThisBinding和GetThisEnvironment,HasThisBinding 方法明确返回false。
函数环境记录envRec的 HasThisBinding 具体方法[…] [执行以下操作]:
如果envRec .[[ThisBindingStatus]] 是词汇的,则返回false;否则返回true。
因此,将迭代地查找外部环境。该过程将结束于三个具有this绑定的环境之一。
这只是意味着,在箭头函数体中,this
来自箭头函数的词法范围,或者换句话说(来自箭头函数与函数声明/表达式:它们是否等效/可交换?):
箭头函数没有自己的
this
[…] 绑定。相反,[此标识符] 像任何其他变量一样在词法作用域中解析。这意味着在箭头函数内部, [引用] 箭头函数定义环境中的this
[值] (即箭头函数“外部”)。this
函数属性
在普通函数(function
、方法)中,由函数的调用方式this
决定。
这就是这些“语法变体”派上用场的地方。
考虑这个包含一个函数的对象:
const refObj = {
func: function(){
console.log(this);
}
};
或者:
const refObj = {
func(){
console.log(this);
}
};
在以下任何函数调用中,this
里面的值都func
将是refObj
. 1
refObj.func()
refObj["func"]()
refObj?.func()
refObj.func?.()
refObj.func
``
如果被调用函数在语法上是基对象的属性,那么这个基对象将是调用的“引用”,通常情况下,它将是 的值this
。这可以通过上面链接的求值步骤来解释;例如,在refObj.func()
(或refObj["func"]()
) 中,CallMemberExpression是整个表达式refObj.func()
,它由MemberExpression refObj.func
和Arguments ()
组成。
而且,refObj.func
各refObj
扮演三个角色:
它们都是表达,
它们都是参考,并且
它们都是价值观。
refObj.func
作为值是可调用函数对象;相应的引用用于确定this
绑定。
可选链和标记模板示例的工作原理非常相似:基本上,引用是 之前?.()
、 之前`
`或 之前的所有内容()
。
从语法上讲, EvaluateCall使用该引用的IsPropertyReference来确定它是否是对象的属性。它试图获取引用的 [[Base]] 属性(例如refObj
,当应用于 时refObj.func
;或foo.bar
当应用于 时foo.bar.baz
)。如果它被写为属性,则GetThisValue将获取此 [[Base]] 属性并将其用作this值。
注意:Getters / Setters 的工作方式与方法相同this
。简单属性不会影响执行上下文,例如,此处this
处于全局范围内:
const o = {
a: 1,
b: this.a, // Is `globalThis.a`.
[this.a]: 2 // Refers to `globalThis.a`.
};
没有基引用的调用、严格模式和with
没有基引用的调用通常是不作为属性调用的函数。例如:
func(); // As opposed to `refObj.func();`.
传递或分配方法或使用逗号运算符时也会发生这种情况。这就是引用记录和值之间的区别所在。
注意函数j
:按照规范,您会注意到j
只能返回函数对象(值)本身,而不能返回引用记录。因此基本引用refObj
会丢失。
const g = (f) => f(); // No base ref.
const h = refObj.func;
const j = () => refObj.func;
g(refObj.func);
h(); // No base ref.
j()(); // No base ref.
(0, refObj.func)(); // Another common pattern to remove the base ref.
EvaluateCall在此处使用未定义的thisValue调用Call 。这在OrdinaryCallBindThis中有所不同(F:函数对象;thisArgument:传递给Call 的thisValue):
令thisMode为F .[[ThisMode]].
[…]
如果thisMode是严格的,则让thisValue成为thisArgument。
别的,
如果thisArgument未定义或为null ,则
让globalEnv成为calleeRealm .[[GlobalEnv]].
[…]
让thisValue为globalEnv .[[GlobalThisValue]].
别的,
让这个值成为!ToObject(这个参数)。
注意:ToObject生成包装对象 [...]。
[…]
注意:本例中,步骤 5在严格模式下 将 thisArgument的实际值设置this
为提供的thisArgumentundefined
。在“松散模式”下,未定义或 null 的thisArgument将导致this
全局this值。
如果IsPropertyReference返回false,则EvaluateCall采取以下步骤:
让refEnv成为ref .[[Base]].
断言:refEnv是一个环境记录。
让thisValue成为refEnv.WithBaseObject ()。
未定义的thisValue可能来自这里:refEnv。WithBaseObject () 始终为undefined,除非在语句中。在这种情况下,thisValue将是绑定对象。with
还有Symbol.unscopables
(MDN 上的文档)来控制with
绑定行为。
总结一下,到目前为止:
function f1(){
console.log(this);
}
function f2(){
console.log(this);
}
function f3(){
console.log(this);
}
const o = {
f1,
f2,
[Symbol.unscopables]: {
f2: true
}
};
f1(); // Logs `globalThis`.
with(o){
f1(); // Logs `o`.
f2(); // `f2` is unscopable, so this logs `globalThis`.
f3(); // `f3` is not on `o`, so this logs `globalThis`.
}
和:
"use strict";
function f(){
console.log(this);
}
f(); // Logs `undefined`.
// `with` statements are not allowed in strict-mode code.
请 注意 , 在 求 值 时this
,普通 函数 的 定义位置并不 重要.
.call
、、、thisArg.apply
和基元.bind
OrdinaryCallBindThis的步骤 5与步骤 6.2(规范中的 6.b)相结合的另一个结果是,原始this值仅在“草率”模式下才会被强制转换为对象。
为了检查这一点,让我们介绍this值的另一个来源:重写this绑定的三个方法:4
Function.prototype.apply(thisArg, argArray)
Function.prototype.
{call
,bind
}(thisArg, ...args)
.bind
创建一个绑定函数,其this绑定设置为thisArg并且不能再改变。.call
并.apply
立即调用该函数,将this绑定设置为thisArg。
.call
并使用指定的thisArg.apply
直接映射到Call。使用BoundFunctionCreate创建绑定函数。它们有自己的[[Call]] 方法,用于查找函数对象的 [[BoundThis]] 内部槽。.bind
设置自定义this值的示例:
function f(){
console.log(this);
}
const myObj = {},
g = f.bind(myObj),
h = (m) => m();
// All of these log `myObj`.
g();
f.bind(myObj)();
f.call(myObj);
h(g);
对于对象来说,在严格和非严格模式下这都是一样的。
现在,尝试提供一个原始值:
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `String { "s" }`.
f.call(myString); // Logs `String { "s" }`.
Object("s")
在非严格模式下,原语会被强制转换为其对象包装形式。它与调用或时获得的对象类型相同new String("s")
。在严格模式下,您可以使用原语:
"use strict";
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `"s"`.
f.call(myString); // Logs `"s"`.
库利用这些方法,例如 jQuery 将设置this
为此处选择的 DOM 元素:
$("button").click(function(){
console.log(this); // Logs the clicked button.
});
构造函数、类和new
当使用运算符将函数作为构造函数调用时new
,EvaluateNew会调用Construct,后者会调用[[Construct]] 方法。如果该函数是基构造函数(即不是class extends
…… ),它会将thisArgument设置为从构造函数的原型创建的新对象。构造函数中设置的属性最终将出现在生成的实例对象上。除非{
您明确返回自己的非原始值,否则将隐式返回。}
`this`this
class
ECMAScript 2015 中引入了一种创建构造函数的新方法。
function Old(a){
this.p = a;
}
const o = new Old(1);
console.log(o); // Logs `Old { p: 1 }`.
class New{
constructor(a){
this.p = a;
}
}
const n = new New(1);
console.log(n); // Logs `New { p: 1 }`.
类定义隐式处于严格模式:
class A{
m1(){
return this;
}
m2(){
const m1 = this.m1;
console.log(m1());
}
}
new A().m2(); // Logs `undefined`.
super
如上所述,使用 的行为的例外new
是class extends
… {
… 。派生类不会在调用时立即设置其this值;它们仅在通过一系列调用到达基类后才这样做(在没有 own 的情况下隐式发生)。不允许在调用之前使用。}
`superconstructor
this`super
调用使用调用的词法范围(函数环境记录)的thissuper
值来调用超级构造函数。GetThisValue对调用有一条特殊规则。它使用BindThisValue来设置该环境记录。super
`this`
class DerivedNew extends New{
constructor(a, a2){
// Using `this` before `super` results in a ReferenceError.
super(a);
this.p2 = a2;
}
}
const n2 = new DerivedNew(1, 2);
console.log(n2); // Logs `DerivedNew { p: 1, p2: 2 }`.
5. 评估类字段
ECMAScript 2022 中引入了实例字段和静态字段。
class
当评估a 时,将执行ClassDefinitionEvaluation ,修改正在运行的执行上下文。对于每个ClassElement:
如果字段是静态的,则
this
引用类本身,如果字段不是静态的,则
this
引用实例。
私有字段(例如#x
)和方法被添加到PrivateEnvironment。
静态块目前是TC39 第三阶段提案。静态块的工作方式与静态字段和方法相同:this
其中引用的是类本身。
请注意,在方法和 getter / setter 中,this
工作方式与在普通函数属性中一样。
class Demo{
a = this;
b(){
return this;
}
static c = this;
static d(){
return this;
}
// Getters, setters, private modifiers are also possible.
}
const demo = new Demo;
console.log(demo.a, demo.b()); // Both log `demo`.
console.log(Demo.c, Demo.d()); // Both log `Demo`.
1 :(o.f)()
等同于o.f()
;(f)()
等同于。这在2ality 文章(存档f()
)中有解释。特别看看ParenthesizedExpression是如何求值的。
2:它必须是MemberExpression,不能是属性,必须具有恰好为"eval"的 [[ReferencedName]] ,并且必须是 %eval% 内部对象。
3:每当规范说“让ref成为求值 X 的结果。”时,那么X就是某个表达式,您需要找到求值步骤。例如,求值MemberExpression或CallExpression是这些算法之一的结果。其中一些算法会产生引用记录。
4:还有其他几种本机和主机方法允许提供this值,特别是Array.prototype.map
、Array.prototype.forEach
等,它们接受thisArg作为其第二个参数。任何人都可以创建自己的方法来进行修改,this
例如、 等。与往常一样,MDN提供了出色的文档。(func, thisArg) => func.bind(thisArg)
`(func, thisArg) => func.call(thisArg)`
只是为了好玩,用一些例子来测试你的理解
对于每个代码片段,回答问题: “标记行的值是多少this
?为什么?” 。
要显示答案,请单击灰色框。
console.log(this); // What is this
here?
}
> `globalThis`. 标记的行在初始全局执行上下文中进行评估。
2. ```
const obj = {};
function myFun(){
return { // What is `this` here?
"is obj": this === obj,
"is globalThis": this === globalThis
};
}
obj.method = myFun;
console.log(obj.method());
运行代码片段Hide results展开片段
obj
. 当将函数作为对象的属性调用时,它将this绑定设置为引用的基数obj.method
,即obj
。
obj = {
myMethod: function(){
return { // What is
this
here?
"is obj": this === obj,
"is globalThis": this === globalThis
};}
},
myFun = obj.myMethod;
console.log(myFun());
运行代码片段Hide results展开片段
> `globalThis`。由于函数值`myFun`/`obj.myMethod`不是从对象中调用的,因此作为属性,*this*绑定将是`globalThis`。这与 Python 不同,在 Python 中,访问方法 ( `obj.myMethod`) 会创建一个绑定方法对象。
4. ```
const obj = {
myFun: () => ({ // What is `this` here?
"is obj": this === obj,
"is globalThis": this === globalThis
})
};
console.log(obj.myFun());
运行代码片段Hide results展开片段
globalThis
。箭头函数不创建自己的this绑定。词法作用域与初始全局作用域相同,也是this
如此globalThis
。
myFun(){
console.log(this); // What is this
here?
}
const obj = {
myMethod: function(){
eval("myFun()");
}
};
obj.myMethod();
> `globalThis`。在评估直接 eval 调用时,`this`是`obj`。但是,在 eval 代码中,`myFun`不是从对象调用的,因此*this*绑定设置为全局对象。
6. ```
function myFun() {
// What is `this` here?
return {
"is obj": this === obj,
"is globalThis": this === globalThis
};
}
const obj = {};
console.log(myFun.call(obj));
运行代码片段Hide results展开片段
obj
。该行myFun.call(obj);
调用特殊的内置函数Function.prototype.call
,该函数接受thisArg
作为第一个参数。
MyCls{
arrow = () => ({ // What is this
here?
"is MyCls": this === MyCls,
"is globalThis": this === globalThis,
"is instance": this instanceof MyCls
});
}
console.log(new MyCls().arrow());
运行代码片段Hide results展开片段
> 它是 的实例`MyCls`。箭头函数不会改变*this*绑定,因此它来自词法作用域。因此,这与上面提到的类字段*完全相同*`a = this;`,例如。尝试将其更改为`static arrow`。您是否获得了预期的结果?
## 解决方案 2:
与其他语言相比,关键字`this`在 JavaScript 中的行为有所不同。在面向对象语言中,`this`关键字指的是类的当前实例。在 JavaScript 中,的值`this`由函数 () 的调用上下文*`context.function()`*及其调用位置决定。
**1. 在全球范围内使用时**
当在全局上下文中使用时`this`,它绑定到全局对象(`window`在浏览器中)
document.write(this); //[object Window]
`this`当您在全局上下文中定义的函数内使用它时,`this`仍然绑定到全局对象,因为该函数实际上是全局上下文的方法。
function f1()
{
return this;
}
document.write(f1()); //[object Window]
以上`f1`是作为全局对象的方法。因此我们也可以在`window`对象上调用它,如下所示:
function f()
{
return this;
}
document.write(window.f()); //[object Window]
**2. 在对象方法中使用时**
`this`当在对象方法内部使用关键字时,`this`将绑定到“直接”封闭对象。
var obj = {
name: "obj",
f: function () {
return this + ":" + this.name;
}
};
document.write(obj.f()); //[object Object]:obj
上面我将“immediate”一词放在了双引号中。这是为了说明,如果你将对象嵌套在另一个对象中,则它将`this`绑定到直接父对象。
var obj = {
name: "obj1",
nestedobj: {
name:"nestedobj",
f: function () {
return this + ":" + this.name;
}
}
}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj
即使你将函数明确地作为方法添加到对象中,它仍然遵循上述规则,即`this`仍然指向直接父对象。
var obj1 = {
name: "obj1",
}
function returnName() {
return this + ":" + this.name;
}
obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1
**3. 调用无上下文函数时**
当您使用`this`在没有任何上下文(即不在任何对象上)调用的内部函数时,它将绑定到全局对象(`window`在浏览器中)(即使该函数是在对象内部定义的)。
var context = "global";
var obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context
}
};
document.write(obj.method()); //[object Window]:global
**使用函数尝试一切**
我们也可以用函数来尝试上述方法。不过还是有一些区别。
* `this`上面我们使用对象字面量表示法向对象添加成员。我们可以通过使用. 来指定函数,从而向函数添加成员。
* 对象字面量表示法会创建一个可以立即使用的对象实例。使用函数时,我们可能需要先使用`new`运算符创建其实例。
* 此外,在对象字面量方法中,我们可以使用点运算符显式地将成员添加到已定义的对象中。这只会添加到特定实例中。但是,我已将变量添加到函数原型中,以便它反映在函数的所有实例中。
下面我尝试了使用 Object 及上面所做的所有事情`this`,但首先创建函数而不是直接编写对象。
/*
When you add variable to the function using this keyword, it
gets added to the function prototype, thus allowing all function
instances to have their own copy of the variables added.
*/
function functionDef()
{
this.name = "ObjDefinition";
this.getName = function(){
return this+":"+this.name;
}
}
obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*
Members explicitly added to the function protorype also behave
as above: all function instances have their own copy of the
variable added.
*/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
return "v"+this.version; //see how this.version refers to the
//version variable added through
//prototype
}
document.write(obj1.getVersion() + "<br />"); //v1
/*
Illustrating that the function variables added by both above
ways have their own copies across function instances
*/
functionDef.prototype.incrementVersion = function(){
this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion(); //incrementing version in obj2
//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1
/*
this
keyword refers to the immediate parent object. If you
nest the object through function prototype, thenthis
inside
object refers to the nested object not the function instance
*/
functionDef.prototype.nestedObj = { name: 'nestedObj',
getName1 : function(){
return this+":"+this.name;
}
};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*
If the method is on an object's prototype chain,
this
refers
to the object the method was called on, as if the method was on
the object.
*/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
//as its prototype
obj3.a = 999; //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
//calling obj3.fun() makes
//ProtoObj.fun() to access obj3.a as
//if fun() is defined on obj3
**4.在构造函数内部使用时**。
当函数作为构造函数使用时(即用关键字调用`new`),`this`函数体内部指向正在构造的新对象。
var myname = "global context";
function SimpleFun()
{
this.myname = "simple function";
}
var obj1 = new SimpleFun(); //adds myname to obj1
//1. new
causes this
inside the SimpleFun() to point to the
// object being constructed thus adding any member
// created inside SimipleFun() using this.membername to the
// object being constructed
//2. And by default new
makes function to return newly
// constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function
**5. 在原型链上定义的函数内部使用时**
如果该方法位于对象的原型链上,`this`则该方法内部引用调用该方法的对象,就好像该方法是在对象上定义的一样。
var ProtoObj = {
fun: function () {
return this.a;
}
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun()
//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but
//this.a
inside fun() retrieves obj3.a
**6. call()、apply() 和 bind() 函数内部**
* 所有这些方法都是在 上定义的`Function.prototype`。
* 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定`this`在执行函数时将使用的值。它们还会在调用时接受要传递给原始函数的任何参数。
* **`fun.apply(obj1 [, argsArray])`**设置为内部`obj1`的值并调用传递的元素作为其参数。`this``fun()``fun()``argsArray`
* **`fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])`**- 设置为内部`obj1`的值并调用传递作为其参数。`this``fun()``fun()``arg1, arg2, arg3, ...`
* **`fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])`**— 返回对绑定到内部函数`fun`并将参数绑定到指定参数的函数的引用。`this``obj1``fun``arg1, arg2, arg3,...`
* 现在`apply`,`call`和之间的区别`bind`一定已经很明显了。`apply`允许将函数的参数指定为类似数组的对象,即具有数字`length`属性和相应的非负整数属性的对象。而`call`允许直接指定函数的参数。`apply`和`call`都会在指定的上下文中使用指定的参数立即调用该函数。另一方面,`bind`只是返回与指定值和参数绑定的函数`this`。我们可以通过将这个返回函数的引用分配给一个变量来捕获它,然后我们可以随时调用它。
function add(inc1, inc2)
{
return this.a + inc1 + inc2;
}
var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
//above add.call(o,5,6) sets `this` inside
//add() to `o` and calls add() resulting:
// this.a + inc1 + inc2 =
// `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6); //g: o.a
i.e. 4 + 5 + 6
document.write(g()+"<br />"); //15
var h = add.bind(o, 5); //h: o.a
i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
// 4 + 5 + 6 = 15
document.write(h() + "<br />"); //NaN
//no parameter is passed to h()
//thus inc2 inside add() is `undefined`
//4 + 5 + undefined = NaN</code>
**7.`this`内部事件处理程序**
* 当您将函数直接分配给元素的事件处理程序时,`this`事件处理函数内部的直接使用引用了相应的元素。这种直接函数分配可以使用`addeventListener`方法或通过传统的事件注册方法(如)来完成`onclick`。
* 类似地,当您直接在元素的`this`事件属性(如)内部使用时,它指的是该元素。`<button onclick="...this..." >`
* 但是,通过在事件处理函数或事件属性内部调用的其他函数间接使用`this`会解析为全局对象`window`。
* 当我们使用 Microsoft 的事件注册模型方法将函数附加到事件处理程序时,可以实现上述相同的行为`attachEvent`。它不是将函数分配给事件处理程序(从而使其成为元素的函数方法),而是在事件上调用该函数(实际上是在全局上下文中调用它)。
*我建议最好在JSFiddle中尝试一下。*
<script>
function clickedMe() {
alert(this + " : " + this.tagName + " : " + this.id);
}
document.getElementById("button1").addEventListener("click", clickedMe, false);
document.getElementById("button2").onclick = clickedMe;
document.getElementById("button5").attachEvent('onclick', clickedMe);
</script>
<h3>Using this
"directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used this
directly in click event property</button>
<h3>Using this
"indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">this
used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">this
used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>
**8. `this`ES6 中的箭头函数**
在箭头函数中,`this`的行为将与普通变量类似:它将从其词法范围继承。`this`箭头函数定义处的 将是箭头函数的`this`。
因此,这与以下行为相同:
(function(){}).bind(this)
请参阅以下代码:
const globalArrowFunction = () => {
return this;
};
console.log(globalArrowFunction()); //window
const contextObject = {
method1: () => {return this},
method2: function(){
return () => {return this};
}
};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject
## 解决方案 3:
JavaScript 的`this`
==================
简单函数调用
------
考虑以下函数:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
请注意,我们在正常模式下运行此程序,即不使用严格模式。
在浏览器中运行时, 的值`this`将被记录为`window`。这是因为`window`是 Web 浏览器范围内的全局变量。
如果您在 node.js 等环境中运行同一段代码,`this`则会引用应用程序中的全局变量。
`"use strict";`现在,如果我们通过在函数声明的开头添加语句来以严格模式运行它,`this`则将不再引用任一环境中的全局变量。这样做是为了避免在严格模式下产生混淆。`this`在这种情况下,只会记录`undefined`,因为它就是这样,它没有定义。
在以下情况下,我们将看到如何操纵的值`this`。
调用对象上的函数
--------
有多种方法可以做到这一点。如果您在 Javascript 中调用过本机方法(如`forEach`和`slice`),那么您应该已经知道,`this`在这种情况下,变量指的是`Object`您调用该函数的 (请注意,在 Javascript 中,几乎所有东西都是`Object`,包括`Array`s 和`Function`s)。以以下代码为例。
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
如果`Object`包含一个包含 的属性`Function`,则该属性称为方法。该方法在调用时,始终会将其`this`变量设置为与其`Object`关联的 。对于严格和非严格模式都是如此。
请注意,如果将方法存储(或复制)到另一个变量中,则对该方法的引用`this`将不再保留在新变量中。例如:
// continuing with the previous code snippet
var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation
考虑更常见的实际情况:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
关键词`new`
---------
考虑一下 Javascript 中的构造函数:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In awal.sayHello
, this
contains the reference to the variable awal
这是怎么回事呢?好吧,让我们看看当我们使用`new`关键字时会发生什么。
1. 使用关键字调用该函数`new`将立即初始化`Object`类型`Person`。
2. 此构造函数的`Object`构造函数设置为`Person`。另请注意,`typeof awal`只会返回`Object`。
3. 这个新的`Object`将被分配到 的原型`Person.prototype`。这意味着`Person`原型中的任何方法或属性都将可供 的所有实例使用`Person`,包括`awal`。
4. 函数`Person`本身现在被调用;`this`作为对新构造对象的引用`awal`。
很简单,是吧?
请注意,官方 ECMAScript 规范中没有任何地方指出此类函数是实际`constructor`函数。它们只是普通函数,`new`可用于任何函数。我们只是这样使用它们,因此我们仅这样称呼它们。
在函数中调用函数:`call`和`apply`
-----------------------
是的,因为`function`s 也是`Objects`(事实上是 Javascript 中的第一类变量),所以即使函数也有方法......嗯,函数本身。
所有函数都从全局的 继承`Function`,它的众多方法中的两个是`call`和,并且都可以用于在调用它们的函数中`apply`操纵 的值。`this`
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
这是使用 的典型示例`call`。它基本上采用第一个参数并`this`在函数中设置`foo`为 的引用`thisArg`。传递给 的所有其他参数都作为参数`call`传递给函数`foo`。
因此,上述代码将记录`{myObj: "is cool"}, [1, 2, 3]`在控制台中。这是在任何函数中更改 的值的非常好的方法`this`。
`apply`与 accept 几乎相同`call`,它只接受两个参数:`thisArg`以及一个包含要传递给函数的参数的数组。因此,上述`call`调用可以转换为`apply`如下形式:
foo.apply(thisArg, [1,2,3])
请注意,`call`和可以覆盖我们在第二点中讨论的通过点方法调用设置`apply`的值。很简单 :)`this`
呈现.... `bind`!
--------------
`bind``call`是and的兄弟`apply`。它也是 JavaScript 中所有函数从全局构造函数继承的方法。 and /`Function`之间的区别在于, and都会实际调用该函数。而 则返回一个带有and预设的新函数。让我们举一个例子来更好地理解这一点:`bind``call``apply``call``apply``bind``thisArg``arguments`
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs function
console.log (bound);
/ logs function () { native code }
/
bound(); // calling the function returned by .bind
// logs {myObj: "even more cool now"}, [1, 2]
发现三者的区别了吗?虽然很微妙,但用法却不同。与`call`和一样`apply`,也会通过点方法调用`bind`覆盖 set 的值。`this`
还要注意,这三个函数都不会对原始函数进行任何更改。and会返回新构造的函数的值,而 while`call`会返回新构造的函数本身,可供调用。`apply``bind`
额外内容,复制此
--------
有时,你不喜欢随着范围而改变的事实`this`,尤其是嵌套范围。请看以下示例。
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup o/
that.hello(); // "world"
});
}
};
在上面的代码中,我们看到 的值随着嵌套范围而改变,但我们想要原始范围中的`this`的值。所以我们“复制”到并使用副本而不是。很聪明,不是吗?`this``this``that``this`
指数:
1. 默认情况下保存了什么`this`?
2. 如果我们用对象点符号将该函数作为方法调用会怎么样?
3. 如果我们使用`new`关键字会怎么样?
4. 我们怎样操纵`this`和?`call``apply`
5. 使用`bind`。
6. 通过复制`this`来解决嵌套范围问题。
## 解决方案 4:
“this” 完全与作用域有关。每个函数都有自己的作用域,而且由于 JS 中的所有内容都是对象,因此即使是函数也可以使用“this”将一些值存储到自身中。OOP 101 教导我们,“this”仅适用于对象的*实例*。因此,每次执行函数时,该函数的新“实例”都会具有“this”的新含义。
大多数人在尝试在匿名闭包函数中使用“this”时会感到困惑,例如:
(函数(值){
这个.值=值;
$('.some-elements').each(函数(elt){
elt.innerHTML = this.value; // 哦哦!可能未定义
});
})(2);
因此,在这里,在 each() 中,“this” 并不保存您期望的“值”(来自
这个.值=值;
因此,为了解决这个(没有双关语意)问题,开发人员可以:
(函数(值){
var self = this; // 小变化
自我.价值 = 价值;
$('.some-elements').each(函数(elt){
elt.innerHTML = self.value; // 呼!!== 2
});
})(2);
尝试一下;你会开始喜欢这种编程模式
## 解决方案 5:
*由于这个话题已经被讨论很久了,我为新读者整理了几点要点`this`。*
的值是如何`this`确定的?
===============
我们使用代词的方式与在英语等自然语言中使用代词的方式类似:“约翰跑得很快,因为***他***想赶上火车。” 我们可以改写为“……***约翰***想赶上火车”。
var person = {
firstName: "Penelope",
lastName: "Barrymore",
fullName: function () {
// We use "this" just as in the sentence above:
console.log(this.firstName + " " + this.lastName);
// We could have also written:
console.log(person.firstName + " " + person.lastName);
}
}
`this` 直到对象调用定义它的函数时,它**才会被赋值**`window`。在全局作用域中,所有全局变量和函数都是在对象上定义的。因此,`this`全局函数引用(并具有)全局`window`对象。
当在全局和未绑定到任何对象的匿名函数中持有 的值`use strict`时。`this``undefined`
在以下情况下该`this`关键字最容易被误解:1)我们借用一个使用的方法`this`,2)我们将一个使用的方法分配`this`给一个变量,3)一个使用的函数`this`作为回调函数传递,以及 4)`this`在闭包(内部函数)内使用。(2)
![桌子](https://i.sstatic.net/nPSkX.png)
未来由什么决定
-------
在ECMA Script 6中定义,箭头函数采用`this`来自封闭(函数或全局)范围的绑定。
function foo() {
// return an arrow function
return (a) => {
// `this` here is lexically inherited from `foo()`
console.log(this.a);
};
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };
var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!
虽然箭头函数提供了一种替代方法`bind()`,但需要注意的是,它们本质上是禁用传统`this`机制,以支持更广泛理解的词法作用域。(1)
---
***参考:***
1. **此 & 对象原型**,作者:Kyle Simpson。© 2014 Getify Solutions。
2. javascriptissexy.com - http://goo.gl/pvl0GX
3. 安格斯克罗尔 - http://goo.gl/Z2RacU
## 解决方案 6:
`this`**在 JavaScript 中始终指正在执行**的函数的“所有者” 。
如果没有定义明确的所有者,则引用最顶层的所有者,即窗口对象。
所以如果我这么做
function someKindOfFunction() {
this.style = 'foo';
}
`element.onclick = someKindOfFunction;`
`this`将引用元素对象。但要小心,很多人都会犯这个错误。
`<element onclick="someKindOfFunction()">`
在后一种情况下,您仅引用该函数,而不将其移交给元素。因此,`this`将引用窗口对象。
## 解决方案 7:
JavaScript 中的每个*执行上下文都有一个**this*参数,其设置方式如下:
1. 函数如何被调用(包括作为对象方法,使用*call*和*apply*,使用*new*)
2. 使用*bind*
3. 箭头函数的词汇表(它们采用其外部执行上下文的*this )*
4. 代码是否处于严格模式或非严格模式
5. 代码是否使用以下方式调用`eval`
您可以使用、或来设置*此*值。`func.call``func.apply``func.bind`
默认情况下,大多数初学者都会感到困惑,当在 DOM 元素上引发事件后调用监听器时,函数的*this值就是 DOM 元素。*
jQuery 使用 jQuery.proxy 使得这一改变变得简单。
## 解决方案 8:
丹尼尔,解释得太棒了!关于这一点,我简单说几句,并列出了`this`事件处理程序中执行上下文指针的良好列表。
简而言之,`this`在 JavaScript 中,指向运行当前函数的对象(或其执行上下文),并且它始终是只读的,无论如何你都无法设置它(这种尝试最终会得到“赋值左侧无效”消息。
**对于事件处理程序:**内联事件处理程序(例如`<element onclick="foo">`)会覆盖之前和之前附加的任何其他处理程序,因此请小心谨慎,最好完全不要使用内联事件委托。感谢 Zara Alaverdyan 通过一场不同意见辩论启发我编写了这份示例列表 :)
* `el.onclick = foo; // in the foo - obj`
* `el.onclick = function () {this.style.color = '#fff';} // obj`
* `el.onclick = function() {doSomething();} // In the doSomething -
Window`
* `el.addEventListener('click',foo,false) // in the foo - obj`
* `el.attachEvent('onclick, function () { // this }') // window, all the
compliance to IE :)`
* `<button onclick="this.style.color = '#fff';"> // obj`
* `<button onclick="foo"> // In the foo - window, but you can <button
onclick="foo(this)">`
## 解决方案 9:
这是一个很好的`this`来源`JavaScript`。
以下是摘要:
* **全球这**
在浏览器中,在全局范围内,`this`对象`window`
<script type="text/javascript">
console.log(this === window); // true
var foo = "bar";
console.log(this.foo); // "bar"
console.log(window.foo); // "bar"
在`node`使用 repl 时,`this`是顶级命名空间。您可以将其引用为`global`。
this
{ ArrayBuffer: [Function: ArrayBuffer],Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 }, Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 }, ...
global === this
true
在`node`从脚本执行时,`this`全局范围以空对象开始。它不同于`global`
\test.js
console.log(this); \ {}
console.log(this === global); \ fasle
* **功能这个**
除了 DOM 事件处理程序的情况或`thisArg`提供的情况(见下文)外,在节点和浏览器中,使用未引用全局范围调用`this`的函数……`new`
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis();
console.log(this.foo); //logs "foo"
</script>
如果你使用`use strict;`,在这种情况下`this`将是`undefined`
<script type="text/javascript">
foo = "bar";
function testThis() {
"use strict";
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
</script>
`new`如果使用调用函数,`this`将会有一个新的上下文,它将不会引用全局的`this`。
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
new testThis();
console.log(this.foo); //logs "bar"
console.log(new testThis().foo); //logs "foo"
</script>
* **原型这个**
您创建的函数将成为函数对象。它们会自动获得一个特殊`prototype`属性,您可以为其分配值。当您通过调用函数创建实例时,`new`您可以访问您分配给该`prototype`属性的值。您可以使用 访问这些值`this`。
function Thing() {
console.log(this.foo);
}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"
console.log(thing.foo); //logs "bar"
在 上分配*数组*或*对象*`prototype`通常是一个错误。如果您希望每个实例都有自己的数组,请在函数中创建它们,而不是在原型中。
function Thing() {
this.things = [];
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
* **反对这个**
您可以`this`在对象上的任何函数中使用 来引用该对象上的其他属性。这与使用 创建的实例不同`new`。
var obj = {
foo: "bar",
logFoo: function () {
console.log(this.foo);
}
};
obj.logFoo(); //logs "bar"
* **DOM 事件**
在 HTML DOM 事件处理程序中,`this`始终是对事件所附加到的 DOM 元素的引用
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick);
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs "<div id="foo"></div>"
}
var listener = new Listener();
document.getElementById("foo").click();
除非你`bind`了解上下文
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs Listener {handleClick: function}
}
var listener = new Listener();
document.getElementById("foo").click();
* **HTML这个**
您可以在 HTML 属性中放置 JavaScript,`this`它是对元素的引用。
<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
* **评估这个**
您可以使用`eval`访问`this`。
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
eval("console.log(this.foo)"); //logs "bar"
}
var thing = new Thing();
thing.logFoo();
* **有了这个**
您可以使用`with`添加`this`到当前范围来读取和写入值,`this`而无需明确引用`this`。
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
with (this) {
console.log(foo);
foo = "foo";
}
}
var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
* **jQuery 这个**
jQuery 在很多地方都会`this`引用 DOM 元素。
<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
this.click();
});
</script>
## 解决方案 10:
**关于 JavaScript 中如何解释“this”**关键字,存在很多困惑。希望本文能一劳永逸地解决所有这些问题。还有更多。请仔细阅读整篇文章。请注意,本文很长。
无论在什么上下文中使用,**“this”**在 JavaScript 中始终引用**“当前对象”**。但是,**“当前对象”**的含义因**上下文**而异。**上下文**可能恰好是以下**6 种情况中的一种**:
1. **全局**(即所有函数之外)
2. **内部直接“非绑定函数”调用(即****未**通过调用***functionName.bind***绑定的函数)
3. 通过***functionName.call***和***functionName.apply*****进行间接“非绑定函数”调用**
4. **在“绑定函数”调用内部**(即通过调用***functionName.bind*****绑定**的函数)
5. **通过“new”创建对象**
6. **内联 DOM 事件处理程序**
下面逐一描述每个上下文:
1. **全局上下文**(即所有函数之外):
在所有函数之外(即在全局上下文中),**“当前对象” (以及****“this”**的值)始终是
浏览器的**“窗口”**对象。
2. **内部直接“非绑定函数”调用**:
在直接“非绑定函数”调用中,**调用该函数的对象将成为“当前对象” (因此也是****“this”**的值)。如果在没有显式**当前对象的**情况下调用函数,则**当前对象**要么是**“window”**对象(对于非严格模式),要么**是未定义(对于严格模式)。在****全局上下文**中定义的任何函数(或变量)
都会自动成为**“window”**对象的属性。例如,假设函数在全局上下文中定义为
function UserDefinedFunction(){
alert(this)
}
它将成为窗口对象的属性,就像您将其定义为
window.UserDefinedFunction=function(){
alert(this)
}
在“非严格模式”下,直接通过 **“UserDefinedFunction()”**调用/调用此函数将自动将其作为**“window.UserDefinedFunction()”**调用/调用,使**“window”**成为
**“UserDefinedFunction”中的****“当前对象” (因此也是****“this”**的值)。在“非严格模式”下调用此函数将导致以下结果
UserDefinedFunction() // displays [object Window] as it automatically gets invoked as window.UserDefinedFunction()
**在“严格模式”下,直接通过“UserDefinedFunction()”**调用/调用该函数
将**“不会”**自动将其调用/调用为**“window.UserDefinedFunction()”**。因此,**“UserDefinedFunction”中的****“当前对象” (以及****“this”**的值)
应为**未定义的**。在“严格模式”下调用此函数将导致以下结果
UserDefinedFunction() // displays undefined
但是,使用窗口对象显式调用它将导致以下结果
window.UserDefinedFunction() // "always displays [object Window] irrespective of mode."
让我们看另一个例子。请看下面的代码
function UserDefinedFunction()
{
alert(this.a + "," + this.b + "," + this.c + "," + this.d)
}
var o1={
a:1,
b:2,
f:UserDefinedFunction
}
var o2={
c:3,
d:4,
f:UserDefinedFunction
}
o1.f() // Shall display 1,2,undefined,undefined
o2.f() // Shall display undefined,undefined,3,4
在上面的例子中,我们看到当通过**o1**调用**“UserDefinedFunction”**时,**“this”将获取****o1**的值,并显示其属性**“a”**和**“b”**的值。 **“c”**和**“d”**的值显示为**未定义**,因为**o1**未定义这些属性
类似地,当通过**o2**调用**“UserDefinedFunction”**时,
**“this”采用****o2**的值,并显示其属性**“c”**和**“d”**的值。 **“a”**和**“b”**的值显示为**未定义**,因为**o2**未定义这些属性。
3. 通过***functionName.call***和***functionName.apply*****进行间接“非绑定函数”调用**:
当通过***functionName.call***或***functionName.apply***调用
**“非绑定函数”**时,**“当前对象”**(以及**“this”的值)将设置为传递给****call/apply 的****“this”**参数(第一个参数)的值
。以下代码演示了相同的操作。
function UserDefinedFunction()
{
alert(this.a + "," + this.b + "," + this.c + "," + this.d)
}
var o1={
a:1,
b:2,
f:UserDefinedFunction
}
var o2={
c:3,
d:4,
f:UserDefinedFunction
}
UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefined
UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
o1.f.call(o2) // Shall display undefined,undefined,3,4
o1.f.apply(o2) // Shall display undefined,undefined,3,4
o2.f.call(o1) // Shall display 1,2,undefined,undefined
o2.f.apply(o1) // Shall display 1,2,undefined,undefined
上面的代码清楚地表明,任何“非绑定函数”的“this”值都可以通过**call/apply**进行更改。此外,如果
没有明确将**“this”参数传递给****call/apply**,则**“当前对象”**(以及“this”的值)在非严格模式下设置为**“window” ,在严格模式下设置为****“undefined”**。
4. **在“绑定函数”调用内部(即通过调用*****functionName.bind***绑定的函数):
**绑定函数是“this”**值已固定的函数。以下代码演示了绑定函数中**“this”的工作方式**
function UserDefinedFunction()
{
alert(this.a + "," + this.b + "," + this.c + "," + this.d)
}
var o1={
a:1,
b:2,
f:UserDefinedFunction,
bf:null
}
var o2={
c:3,
d:4,
f:UserDefinedFunction,
bf:null
}
var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1
bound1() // Shall display 1,2,undefined,undefined
var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
bound2() // Shall display undefined,undefined,3,4
var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
bound3() // Shall display undefined,undefined,3,4
var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
bound4() // Shall display 1,2,undefined,undefined
o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
o1.bf() // Shall display undefined,undefined,3,4
o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
o2.bf() // Shall display 1,2,undefined,undefined
bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function
如上代码所示,**任何“绑定函数”的“this”值都无法通过 call/apply 进行更改**。此外,如果
未明确将**“this”参数传递给 bind,则****“当前对象”**
(以及**“this”**的值)在非严格模式下设置为**“window” ,在严格模式下设置为****“undefined”**。还有一件事。绑定已绑定的函数不会更改**“this”**的值。它仍设置为第一个 bind 函数设置的值。
5. **通过“new”创建对象时**:
在构造函数中,**“当前对象” (以及****“this”**的值
)引用当前通过**“new”**创建的对象,而不管函数的绑定状态如何。但是,如果构造函数是绑定函数,则应使用为绑定函数设置的预定义参数集来调用它。
6. **内联 DOM 事件处理程序内部**:
请查看以下 HTML 代码片段
<button onclick='this.style.color=white'>Hello World</button>
<div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>
上面例子中的“ **this”**分别指的是“button”元素和“div”元素。
第一个例子中,当按钮被点击时,按钮的字体颜色应设置为白色。
在第二个示例中,当单击**“div”元素时,它将调用****OnDivClick**函数,其第二个参数引用单击的 div 元素。但是,
OnDivClick 中的**“this”**值**不得**引用单击的**div**
元素。它应分别在**非严格**模式和**严格模式**下设置为**“window 对象”**或
**“undefined”**(如果**OnDivClick**是**未绑定函数**)或设置为预定义的绑定值(如果**OnDivClick**是**绑定函数**)
以下总结整篇文章
1. 在全局上下文中,**“this”**始终引用**“window”**对象
2. 每当调用一个函数时,它都会在对象(**“当前对象”**)的上下文中调用。如果没有明确提供**当前对象,则****当前对象默认****为非严格模式**下的**“窗口对象”** ,严格模式下的**“未定义” 。**
3. **非绑定函数中“this”**的值是对该函数被调用上下文中的对象的引用(**“当前对象”**)
4. 非绑定函数中
**“this”**的值可以被函数的**调用**和**应用方法覆盖。**
5. 对于 Bound 函数来说,**“this”**的值是固定的,不能被函数的**call**和**apply方法覆盖。**
6. 绑定和已绑定的函数不会改变“this”的值。它仍然设置为第一个绑定函数设置的值。
7. **构造函数中“this”**的值是正在创建和初始化的对象
8. **内联 DOM 事件处理程序中“this”**的值是指赋予该事件处理程序的元素。
## 解决方案 11:
可能最详细、最全面的文章`this`是以下内容:
JavaScript 中“this”关键字的详解
背后的想法`this`是了解函数调用类型对于设置`this`值具有重要意义。
---
当难以识别时`this`,**不要**问自己:
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件