我希望找到一个“this”关键字的明确解释,以及如何正确使用它。

它的行为似乎很奇怪,我不完全明白为什么。

这是如何工作的,何时使用?


当前回答

关于此关键字的一些信息

让我们将此关键字记录到全局范围内的控制台中,无需任何代码

console.log(this)

在客户端/浏览器中,此关键字是一个全局对象,它是窗口

console.log(this === window) // true

and

在Server/Node/Javascript运行时中,此关键字也是一个全局对象,即module.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

请记住,导出只是对module.exports的引用

其他回答

丹尼尔,太棒了!在事件处理程序的情况下,这里有几个单词,以及这个执行上下文指针的良好列表。

换句话说,JavaScript中的这一点指向运行当前函数的对象(或其执行上下文),并且它始终是只读的,无论如何都不能设置它(这样的尝试将以“赋值中的左侧无效”消息结束)。

对于事件处理程序:内联事件处理程序,例如<elementonclick=“foo”>,会覆盖之前附加的任何其他处理程序,因此要小心,最好不要进行内联事件委派。感谢Zara Alaverdyan,他通过一场不同意见的辩论激发了我的灵感:)

el.onclick=foo;//在foo-obj中el.onclick=函数(){this.style.color=“#fff”;}//objel.onclick=function(){doSomething();}//在doSometing中-窗el.addEventListener('click',foo,false)//在foo-obj中el.attachEvent('包含,函数(){//this}')//窗口,所有符合IE:)<button onclick=“this.style.color='#fff';”>//obj<button onclick=“foo”>//在foo窗口中,但您可以<buttononclick=“foo(this)”>

“这”是关于范围的。每个函数都有自己的作用域,因为JS中的所有内容都是一个对象,所以即使是一个函数也可以使用“this”将一些值存储到自身中。OOP 101教导“这”仅适用于对象的实例。因此,每次函数执行时,该函数的新“实例”都具有“this”的新含义。

大多数人在尝试在匿名闭包函数中使用“this”时会感到困惑,例如:

(function(value) {
    this.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = this.value;        // uh oh!! possibly undefined
    });
})(2);

所以这里,在each()内部,“this”不包含您期望的“value”(从this.value=value;在其上方)。因此,为了克服这个(并非双关语)问题,开发人员可以:

(function(value) {
    var self = this;            // small change
    self.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = self.value;        // phew!! == 2 
    });
})(2);

试试看;你会开始喜欢这种编程模式

在伪经典术语中,许多讲座教授“this”关键字的方式是作为类或对象构造函数实例化的对象。每次从一个类构造一个新对象时,想象一下在后台创建并返回一个“this”对象的本地实例。我记得它是这样教的:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;

这是JavaScript中的关键字,是执行上下文的属性。它的主要用途是函数和构造函数。这方面的规则非常简单(如果你坚持最佳实践)。

规范中的技术说明

ECMAScript标准通过抽象操作(缩写为AO)ResolveThisBinding对此进行了定义:

[AO]ResolveThisBinding[…]使用正在运行的执行上下文的LexicalEnvironment确定关键字this的绑定。[步骤]:让envRec为GetThisEnvironment()。回来envRec.GetThisBinding()。

全局环境记录、模块环境记录和函数环境记录都有自己的GetThisBinding方法。

GetThisEnvironment AO查找当前运行的执行上下文的LexicalEnvironments,并查找最接近的优势环境记录(通过迭代访问其[[OuterEnv]]财产),该记录具有此绑定(即HasThisBinding返回true)。此过程以三种环境记录类型之一结束。

这个值通常取决于代码是否处于严格模式。

GetThisBinding的返回值反映了当前执行上下文的this值,因此无论何时建立新的执行上下文,它都会解析为一个不同的值。修改当前执行上下文时也可能发生这种情况。以下小节列出了可能发生这种情况的五种情况。

您可以将代码样本放在AST资源管理器中,以遵循规范细节。

1.脚本中的全局执行上下文

这是在顶层评估的脚本代码,例如直接在<script>中:

<script>
// Global context
console.log(this); // Logs global object.

setTimeout(function(){
  console.log("Not global context");
});
</script>

在脚本的初始全局执行上下文中,计算此值会导致GetThisBinding采取以下步骤:

全局环境记录envRec的GetThisBinding具体方法[…][执行此操作]:返回envRec。[[GlobalThisValue]]。

全局环境记录的[[GlobalThisValue]]属性始终设置为主机定义的全局对象,该对象可通过globalThis(Web上的窗口,Node.js上的全局;MDN上的文档)访问。按照InitializeHostDefinedRealm的步骤,了解[[GlobalThisValue]]属性是如何产生的。

2.模块中的全局执行上下文

ECMAScript 2015中引入了模块。

这适用于模块,例如直接在<script type=“module”>内部时,而不是简单的<script>。

当在模块的初始全局执行上下文中时,计算此值会导致GetThisBinding采取以下步骤:

模块Environment Record[…]的GetThisBinding具体方法[执行此操作]:返回未定义。

在模块中,this的值始终在全局上下文中未定义。模块隐式处于严格模式。

3.输入eval代码

有两种eval调用:直接调用和间接调用。这种区别自ECMAScript第5版以来就存在。

直接eval调用通常看起来像eval(…);或(eval)(…);(或((eval))(…);,1只有在调用表达式符合窄模式时才是直接的。2间接eval调用涉及以任何其他方式调用函数引用eval。可能是eval?。(…),(…,eval)(…)、window.eval(…)和eval.call(…,…)等。给定常量aliasVal1=eval;window.aliasVal2=eval;,它也可以是aliasVal1(…)、aliasVal2(…)。另外,给定const originalEval=eval;window.eval=(x)=>原始评估(x);,调用eval(…)也是间接的。

请参阅chuckj对“JavaScript中的(1,eval)('this')vs eval('this')?”和Dmitry Soshnikov的ECMA-262-5的详细回答–第2章:严格模式(存档),了解何时可以使用间接eval()调用。

PerformEval执行eval代码。它创建一个新的声明性环境记录作为其LexicalEnvironment,GetThisEnvironment从中获取此值。

然后,如果这出现在eval代码中,则调用GetThisEnvironment找到的环境记录的GetThisBinding方法并返回其值。

创建的声明性环境记录取决于eval调用是直接调用还是间接调用:

在直接求值中,它将基于当前运行的执行上下文的LexicalEnvironment。在间接求值中,它将基于执行间接求值的Realm Record的[[GlobalEnv]]属性(全局环境记录)。

这意味着:

在直接求值中,this值不变;它取自称为eval的词法范围。在间接求值中,this值是全局对象(globalThis)。

新功能如何? — newFunction类似于eval,但它不会立即调用代码;它创建了一个函数。此绑定不适用于此处的任何地方,除非调用函数时正常工作,如下一小节所述。

4.输入功能代码

调用函数时会输入函数代码。

调用函数有四类语法。

EvaluateCall AO针对以下三项进行:3正常函数调用可选链接调用标记的模板对这一项执行EvaluateNew:3构造函数调用

实际函数调用发生在调用AO处,调用AO时使用上下文确定的thisValue;这个参数在一长串与调用相关的调用中传递。Call调用函数的[[Call]]内部插槽。这将调用PrepareForOrdinaryCall,其中创建了一个新函数Environment Record:

函数环境记录是一个声明性环境记录,用于表示函数的顶级范围,如果函数不是ArrowFunction,则提供此绑定。如果函数不是ArrowFunction函数并引用了super,则其函数Environment Record还包含用于从函数内执行超级方法调用的状态。

此外,函数Environment Record中还有[[ThisValue]]字段:

这是用于此函数调用的This值。

NewFunctionEnvironment调用还设置函数环境的[[ThisBindingStatus]]属性。

[[Call]]还调用OrdinaryCallBindThis,其中适当的thisArgument是基于以下内容确定的:

原始参考,函数的类型,以及无论代码是否处于严格模式。

一旦确定,对新创建的函数Environment Record的BindThisValue方法的最后调用实际上会将[[ThisValue]]字段设置为thisArgument。

最后,这个字段是函数Environment Record的GetThisBinding AO从中获取值的位置:

函数Environment Record envRec〔…〕〔执行此操作〕的GetThisBinding具体方法:[…]3.返回envRec。[[ThisValue]]。

同样,如何精确地确定该值取决于许多因素;这只是一个概述。有了这个技术背景,让我们来看看所有具体的例子。

箭头功能

计算箭头函数时,函数对象的[[ThisMode]]内部槽在OrdinaryFunctionCreate中设置为“词法”。

在OrdinaryCallBindThis,它接受函数F:

设thisMode为F.[[thisMode]]。如果thisMode是词法的,则返回NormalCompletion(未定义)。[…]

这仅仅意味着绑定此的算法的其余部分被跳过。箭头函数不绑定自己的此值。

那么,在箭头函数中这是什么呢?回顾ResolveThisBinding和GetThisEnvironment,HasThisBinding方法显式返回false。

函数Environment Record envRec[…]的HasThisBinding具体方法[执行此操作]:如果envRec。[[ThisBindingStatus]]是词法的,返回false;否则,返回true。

因此,外部环境是迭代查找的。该过程将在具有此绑定的三个环境之一中结束。

这仅仅意味着,在箭头函数体中,这来自箭头函数的词法范围,或者换句话说(来自箭头函数与函数声明/表达式:它们是否等价/可交换?):

箭头函数没有自己的this[…]绑定。相反,[这个标识符]像任何其他变量一样在词法范围内解析。这意味着,在箭头函数内部,this[指的是]在定义箭头函数的环境中的[this值](即“在”箭头函数外部)。

函数财产

在普通函数(函数、方法)中,这取决于函数的调用方式。

这就是这些“语法变体”派上用场的地方。

考虑此对象包含函数:

const refObj = {
    func: function(){
      console.log(this);
    }
  };

或者:

const refObj = {
    func(){
      console.log(this);
    }
  };

在以下任何函数调用中,func中的this值将为refObj.1

refObj.func()refObj[“func”]()参考对象?。函数()参考对象函数?。()参考目标函数``

如果被调用的函数在语法上是基对象的属性,那么这个基将是调用的“引用”,在通常情况下,它将是这个的值。上述评估步骤解释了这一点;例如,在refObj.func()(或refObj[“func”]())中,CallMemberExpression是整个表达式refObj.func(),它由MemberExpression refObj.unc和Arguments()组成。

此外,refObj.func和refObj分别扮演三个角色:

它们都是表达式,他们都是推荐人,而且它们都是价值观。

refObj.func作为值是可调用函数对象;使用相应的引用来确定该绑定。

可选的链接和标记模板示例的工作方式非常相似:基本上,引用是?之前的所有内容?。()、“”之前或()之前。

EvaluateCall使用该引用的IsPropertyReference从语法上确定它是否是对象的属性。它正在尝试获取引用的[[Base]]属性(例如,当应用于refObj.func时为refObj;当应用于foo.bar.baz时为foo.bar)。如果将其作为属性写入,则GetThisValue将获取此[[Base]]属性并将其用作this值。

注意:关于这一点,Getters/Setter的工作方式与方法相同。简单的财产不会影响执行上下文,例如,这里是全局范围:

const o = {
    a: 1,
    b: this.a, // Is `globalThis.a`.
    [this.a]: 2 // Refers to `globalThis.a`.
  };

没有基引用、严格模式和

没有基引用的调用通常是不作为属性调用的函数。例如:

func(); // As opposed to `refObj.func();`.

传递或分配方法或使用逗号运算符时也会发生这种情况。这就是参考记录和值之间的差异。

注意函数j:按照规范,您将注意到j只能返回函数对象(Value)本身,而不能返回引用记录。因此,基础引用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被称为Realm。[[GlobalEnv]]。[…]让thisValue为globalEnv。[[GlobalThisValue]]。其他的让这个价值成为现实!ToObject(thisArgument)。注意:ToObject生成包装对象[…]。[…]

注意:步骤5将this的实际值设置为严格模式下提供的thisArgument — 在这种情况下未定义。在“草率模式”中,未定义或空的thisArgument将导致此值为全局this值。

如果IsPropertyReference返回false,则EvaluateCall将执行以下步骤:

设refEnv为ref[[Base]]。断言:refEnv是一个环境记录。让thisValue为refEnv.WithBaseObject()。

这就是未定义的thisValue可能来自的地方:refEnv.WithBaseObject()始终未定义,with语句除外。在这种情况下,thisValue将是绑定对象。

还有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`.
}

and:

"use strict";

function f(){
  console.log(this);
}

f(); // Logs `undefined`.

// `with` statements are not allowed in strict-mode code.

请注意,在计算此值时,在哪里定义正常函数并不重要。

.call、.apply、.bind、thisArg和基元

OrdinaryCallBindThis的第5步与第6.2步(规范中的6.b)相结合的另一个结果是,一个基元只有在“草率”模式下才会将此值强制到一个对象。

为了检查这一点,让我们介绍this值的另一个源:重写this绑定的三个方法:4

函数.原型.应用(thisArg,argArray)Function.prototype.{call,bind}(thisArg,…args)

.bind创建一个绑定函数,其此绑定设置为thisArg,不能再次更改。call和.apply立即调用函数,并将此绑定设置为thisArg。

.call和.apply map直接应用于call,使用指定的thisArg。bind使用BoundFunctionCreate创建绑定函数。它们有自己的[[Call]]方法,该方法查找函数对象的[[BoundThis]]内部槽。

设置自定义此值的示例:

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运算符作为构造函数调用函数时,EvaluateNew调用Construct,后者调用[[Construct]]方法。如果函数是基构造函数(即不是类扩展…{…}),它将thisArgument设置为从构造函数的原型创建的新对象。在构造函数中对此设置的财产将结束于结果实例对象。这是隐式返回的,除非您显式返回自己的非基元值。

类是创建构造函数的一种新方法,在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`.

超级的

使用new的行为的例外是类扩展…{…},如上所述。派生类在调用时不会立即设置其此值;它们只在通过一系列超级调用到达基类时才会这样做(在没有自己的构造函数的情况下隐式发生)。不允许在调用super之前使用此选项。

调用super将使用调用的词法范围(函数Environment Record)的此值调用超级构造函数。GetThisValue对超级调用有一个特殊的规则。它使用BindThisValue将其设置为该环境记录。

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中引入了实例字段和静态字段。

对类求值时,执行ClassDefinitionEvaluation,修改正在运行的执行上下文。对于每个ClassElement:

如果一个字段是静态的,那么它指的是类本身,如果一个字段不是静态的,那么这将引用该实例。

私有字段(例如#x)和方法被添加到PrivateEnvironment中。

静态块目前为TC39第3阶段提案。静态块的工作方式与静态字段和方法相同:它们内部的这一点是指类本身。

注意,在方法和getters/setter中,这就像在普通函数财产中一样工作。

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) ()相当于f()。这在本文(存档)中进行了解释。请特别查看如何计算带圆括号的表达式。

2:它必须是MemberExpression,不能是属性,必须具有正好为“eval”的[[ReferencedName],并且必须是%eval%内在对象。

3:每当规范中说“让ref是对X求值的结果”,那么X就是需要找到求值步骤的表达式。例如,计算MemberExpression或CallExpression是这些算法之一的结果。其中一些会生成参考记录。

4:还有其他几种本机和主机方法允许提供this值,特别是Array.prototype.map、Array.prototype.forEach等,它们接受thisArg作为第二个参数。任何人都可以使用自己的方法来改变这一点,比如(func,thisArg)=>func.bind(thisArg),(func、thisAg)=>func.call(thisAg)等。一如既往,MDN提供了很好的文档。


只是为了好玩,用一些例子来测试你的理解

对于每个代码段,回答以下问题:“标记行的值是多少?为什么?”。

要显示答案,请单击灰色框。

if(真){console.log(this);//这是什么?}globalThis。标记行在初始全局执行上下文中求值。常量obj={};函数myFun(){return{//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis};}obj.method=myFun;console.log(obj.method());当将函数作为对象的属性调用时,将此绑定设置为引用obj.method的基,即obj。常量对象={myMethod:函数(){return{//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis};}},myFun=obj.myMethod;console.log(myFun());globalThis。由于函数值myFun/obj.myMMethod未从对象中调用,因此作为属性,此绑定将为globalThis。这与Python不同,在Python中,访问方法(obj.myMethod)会创建绑定的方法对象。常量对象={myFun:()=>({//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis})};console.log(obj.myFun());globalThis。箭头函数不会创建自己的此绑定。词法作用域与初始全局作用域相同,因此这是globalThis。函数myFun(){console.log(this);//这是什么?}常量对象={myMethod:函数(){eval(“myFun()”);}};obj.myMethod();globalThis。当计算直接eval调用时,这是obj。然而,在eval代码中,myFun没有从对象中调用,因此this绑定被设置为全局对象。函数myFun(){//这是什么?返回{“is obj”:this==obj,“is globalThis”:this==globalThis};}常量obj={};console.log(myFun.call(obj));obj.行myFun.call(obj);正在调用特殊的内置函数function.prototype.call,该函数接受thisArg作为第一个参数。类MyCls{arrow=()=>({//这里的“this”是什么?“是MyCls”:这==MyCls,“is globalThis”:this==globalThis,“is instance”:MyCls的此实例});}console.log(新MyCls().arrow());这是MyCls的实例。箭头函数不会更改此绑定,因此它来自词法范围。因此,这与上面提到的类字段完全相同,如a=this;。尝试将其更改为静态箭头。你得到你期望的结果了吗?

Javascript就是这个

简单函数调用

考虑以下功能:

function foo() {
    console.log("bar");
    console.log(this);
}
foo(); // calling the function

请注意,我们在正常模式下运行,即不使用严格模式。

在浏览器中运行时,此值将作为窗口记录。这是因为window是web浏览器范围内的全局变量。

如果在node.js这样的环境中运行相同的代码,这将引用应用程序中的全局变量。

现在,如果我们通过添加语句“usestrict”在严格模式下运行此操作;在函数声明的开头,这将不再引用这两个环境中的全局变量。这样做是为了避免严格模式中的混淆。在这种情况下,这将只是log undefined,因为这就是它,它没有被定义。

在下面的例子中,我们将看到如何操纵这个值。

对对象调用函数

有不同的方法可以做到这一点。如果您在Javascript中调用了本机方法,如forEach和slice,那么您应该已经知道,在这种情况下,this变量指的是调用该函数的Object(注意,在Javascript中,几乎所有的东西都是Object,包括数组和函数)。以以下代码为例。

var myObj = {key: "Obj"};
myObj.logThis = function () {
    // I am a method
    console.log(this);
}
myObj.logThis(); // myObj is logged

如果对象包含包含函数的属性,则该属性称为方法。调用此方法时,将始终将此变量设置为与其关联的对象。对于严格模式和非严格模式都是如此。

请注意,如果一个方法存储(或复制)在另一个变量中,则对该方法的引用将不再保留在新变量中。例如:

// 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

新关键字

考虑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关键字调用函数将立即初始化Person类型的Object。此对象的构造函数将其构造函数设置为Person。此外,请注意,typeof awal将仅返回Object。这个新对象将被分配Person.prototype的原型。这意味着Person原型中的任何方法或属性都可用于Person的所有实例,包括awal。现在调用函数Person本身;这是对新构建的对象awal的引用。

很简单,嗯?

请注意,官方的ECMAScript规范中没有说明此类函数是实际的构造函数。它们只是普通函数,新函数可以用于任何函数。只不过我们是这样使用它们的,所以我们只是这样称呼它们。

在函数上调用函数:调用和应用

所以,是的,因为函数也是Object(实际上是Javascript中的第一类变量),所以即使函数也有方法。。。嗯,功能本身。

所有函数都继承自全局函数,它的许多方法中有两个是调用和应用,这两个方法都可以用于在调用它们的函数中操纵此值。

function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);

这是使用call的典型示例。它基本上接受第一个参数,并在函数foo中将其设置为对thisArg的引用。传递给调用的所有其他参数都作为参数传递给函数foo。因此,上面的代码将在控制台中记录{myObj:“is cool”},[1,2,3]。在任何函数中更改此值的非常好的方法。

apply与callaccept几乎相同,它只接受两个参数:thisArg和一个包含要传递给函数的参数的数组。因此,可以将上述调用转换为如下应用:

foo.apply(thisArg, [1,2,3])

请注意,调用和应用可以覆盖我们在第二个项目中讨论的点方法调用集的值。足够简单:)

呈现。。。。绑定

bind是应召而来的兄弟。它也是Javascript中所有函数从全局函数构造函数继承的方法。绑定和调用/应用之间的区别在于,调用和应用实际上都会调用函数。另一方面,bind会返回一个预设了thisArg和参数的新函数。让我们举个例子来更好地理解这一点:

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]`

看到三者之间的区别了吗?这很微妙,但它们的用法不同。与调用和应用一样,bind也将通过点方法调用覆盖此集合的值。

还要注意,这三个函数都没有对原始函数进行任何更改。call和apply将返回新构造的函数的值,而bind将返回新构建的函数本身,以便调用。

额外的东西,复制这个

有时,您不喜欢这样一个事实,即这会随范围而改变,尤其是嵌套范围。看看下面的例子。

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"
        });
    }
  };

在上面的代码中,我们看到这个值随嵌套范围而改变,但我们希望从原始范围中获得这个值。所以我们将这个“复制”到那个,并使用副本代替这个。聪明,嗯?

索引:

默认情况下,此中包含什么?如果我们将函数作为一个带有Object点符号的方法调用呢?如果我们使用新关键字呢?我们如何通过调用和应用来处理这一点?使用bind。复制此以解决嵌套范围问题。