1494

我正在寻找一个关于“this”关键字的作用以及如何正确使用它的清晰解释。

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

如何工作以及何时使用?

2

22个答案22

重置为默认值
1496

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

技术说明在规范中

这个ECMAScript标准定义通过抽象操作(缩写AO公司)解析此绑定:

[AO]ResolveThisBinding[…]确定关键字的绑定使用的词汇环境运行执行上下文.[步骤]:

  1. 环境回收获取此环境().
  2. 返回?环境记录.GetThisBinding()。

全球环境记录,模块环境记录、和功能环境记录每个都有自己的GetThisBinding方法。

这个获取此环境AO查找电流运行执行上下文的LexicalEnvironment并查找最接近的优势环境记录(通过迭代访问其[[OuterEnv]]属性),该记录具有绑定(即HasThisBinding返回真的). 此过程以三种环境记录类型之一结束。

的价值通常取决于代码是否在严格模式.

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

您可以将代码示例放在AST资源管理器遵循规范细节。

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

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

<脚本>//全球背景console.log(this);//记录全局对象。setTimeout(函数(){console.log(“非全局上下文”);});</script>

在脚本的初始全局执行上下文中,计算原因获取此绑定采取以下步骤:

全局环境记录的GetThisBinding具体方法环境回收[…][这样做]:

  1. 返回环境记录.[[GlobalThisValue]]。

全局环境记录的[[GlobalThisValue]]属性始终设置为主机定义的全局对象,可通过访问全球This(窗口在Web上,全球的在Node.js上;MDN上的文档). 遵循的步骤初始化HostDefinedRealm了解[[GlobalThisValue]]属性的形成过程。

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

ECMAScript 2015中引入了模块。

这适用于模块,例如直接在<script type=“模块”>,而不是简单的<脚本>.

在模块的初始全局执行上下文中,计算原因获取此绑定采取以下步骤:

模块Environment Record[…][的GetThisBinding具体方法是这样做的]:

  1. 返回未定义.

在模块中总是未定义在全球范围内。模块隐式位于严格模式.

3.进入评估代码

有两种评估电话:直接的间接的这一区别自ECMAScript第5版以来就一直存在。

  • A直接评估呼叫通常看起来像评估();(评估)();(或((评估)();等)。1它只是直接的如果调用表达式符合窄模式。2
  • 间接的评估调用涉及调用函数引用评估以任何其他方式。可能是这样评估?。(),(,评估)(),窗口.eval(),评估呼叫(,)等给出常量别名Eval1=eval;window.aliasEval2=评估;,也会是别名评估1(),别名Eval2().单独给出const originalEval=评估;window.eval=(x)=>原始评估(x);,正在呼叫评估()也会是间接的。

请参见查克的答案“JavaScript中的(1,eval)('this')vs eval('this')?”德米特里·索什尼科夫的ECMA-262-5详细介绍——第2章:严格模式(已存档)当你可能使用间接评估()呼叫。

性能评估执行评估代码。它创建了一个新的声明性环境记录作为其词汇环境获取此环境获得值。

那么,如果出现在中评估代码,找到的Environment Record的GetThisBinding方法获取此环境调用并返回其值。

以及创建的声明性环境记录取决于评估呼叫是直接或间接的:

这意味着:

  • 在直接评估中值不变;它取自调用的词法范围评估.
  • 在间接评估中值是全局对象(全球This).

关于新建函数? — 新建函数类似于评估,但它不会立即调用代码;它创建了一个函数。一个绑定在这里任何地方都不适用,除非调用该函数时,该函数正常工作,如下一小节所述。

4.进入功能代码

在以下情况下输入功能代码呼叫函数。

调用函数有四类语法。

实际的函数调用发生在呼叫AO,使用this值根据上下文确定;这个参数通过一长串与调用相关的调用传递。呼叫调用[[呼叫]]函数的内部插槽。这个电话准备日常通话其中一个新的功能环境记录创建时间:

一个功能环境记录是一个声明性环境记录,用于表示函数的顶级范围,如果函数不是箭头函数,提供了结合。如果函数不是箭头函数功能和参考超级的,其功能环境记录还包含用于执行的状态超级的方法调用。

此外,函数Environment Record:

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

这个新功能环境调用还设置函数环境的[[ThisBindingStatus]]属性。

[[呼叫]]也会呼叫常规CallBindThis,在适当的情况下this参数取决于:

  • 原始参考,
  • 函数的类型,以及
  • 代码是否在严格模式.

一旦确定,对绑定ThisValue新创建的函数Environment Record的方法实际将[[ThisValue]]字段设置为this参数.

最后,在这个字段中功能环境记录 获取此绑定AO获取的值发件人:

函数Environment Record的GetThisBinding具体方法环境记录[…][这样做]:

[…]
3.退货环境回收.[[ThisValue]]。

再说一遍价值取决于许多因素;这只是一个概述。有了这样的技术背景,我们来看看所有具体的例子。

箭头函数

箭头函数则函数对象的[[ThisMode]]内部插槽设置为“词汇”在里面常规函数创建.

常规CallBindThis,它接受一个函数F类:

  1. this模式F类.[[此模式]]。
  2. 如果this模式词汇的,返回NormalCompletion(未定义).[…]

这意味着绑定的其余算法已跳过。箭头函数不绑定自己的值。

那么,什么是在一个箭头函数中,那么?回顾解析此绑定获取此环境,的HasThisBinding方法显式返回.

函数环境记录的HasThisBinding具体方法环境记录[…][这样做]:

  1. 如果环境记录.[[ThisBindingStatus]]是词汇的,返回; 否则,返回真的.

因此,外部环境是以迭代方式查找的。该过程将在具有结合。

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

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

功能属性

在正常功能中(功能,方法),已确定通过调用函数的方式.

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

考虑此对象包含一个函数:

常量refObj={func:函数(){console.log(this);}};

或者:

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

在以下任何函数调用中内部的值函数参考对象.1

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

如果被调用的函数在语法上是基对象的属性,那么这个基将是调用的“引用”,在通常情况下,它将是上述评估步骤对此进行了解释;例如,在参考对象函数()(或参考对象[“函数”]()),的调用成员表达式是整个表达式参考对象函数(),包括成员表达式 参考对象.func论据 ().

但同时,参考对象.func参考对象扮演三个角色,每个角色:

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

参考对象.func作为一个价值是可调用函数对象;相应的参考用于确定结合。

可选链接和标记模板示例的工作原理非常类似:基本上,引用是?.(),在``,或在().

EvaluateCall(评估呼叫)使用IsPropertyReference(属性引用)以确定它在语法上是否是对象的属性。它试图获取引用的[[Base]]属性(例如。参考对象,应用于时参考对象.func; foo.bar按钮应用于时foo.bar.baz公司). 如果它是作为属性写入的,那么获取此值将获取此[[Base]]属性并将其用作值。

注:Getters/Setters公司以与方法相同的方式工作。简单属性不会影响执行上下文,例如,在全球范围内:

常数o={a: 1、,b: this.a,//是`globalThis.a`。[this.a]:2//表示`globalThis.a`。};

没有基本引用、严格模式和具有

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

func();//与`refObj.func();`相反。

传递或分配方法,或使用逗号运算符。这与参考记录和值之间的差异有关。

Note函数j个:按照规范,您会注意到j个只能返回函数对象(Value)本身,而不能返回引用记录。因此,基准参考参考对象丢失了。

常数g=(f)=>f();//无基准参考。常数h=refObj.func;常量j=()=>参考对象.func;g(参考对象函数);h();//无基准参考。j()();//无基准参考。(0,引用对象函数)();//另一个常见的模式是删除基础引用。

EvaluateCall(评估呼叫)电话呼叫用一个this值属于未定义在这里。这在常规CallBindThis(F类:功能对象;this参数:的this值传递给呼叫):

  1. this模式F类.[[此模式]]。

[…]

  1. 如果this模式严格的,让this值this参数.
  2. 否则,
    1. 如果this参数未定义无效的,然后
      1. 全球环境被称为Realm.[[GlobalEnv]]。
      2. […]
      3. this值全球环境.[[GlobalThisValue]]。
    2. 否则,
      1. this值成为!ToObject(目标对象)(thisArgument)。
      2. 注意:ToObject(目标对象)生成包装器对象[…]。

[…]

注:步骤5设置至提供的this参数在严格模式下 — 未定义在这种情况下。在“sloppy mode”中,未定义或空this参数中的个结果全球化值。

如果IsPropertyReference(属性引用)收益,然后EvaluateCall(评估呼叫)采取以下步骤:

  1. 参考环境裁判.[[基础]]。
  2. 断言:参考环境是环境记录。
  3. this值参考环境.WithBaseObject()。

这是未定义的this值可能来自:参考环境.有基本对象()总是未定义,除了在里面具有声明。在这种情况下,this值将是绑定对象。

还有符号.不透明度(MDN上的文档)控制具有绑定行为。

总结一下,到目前为止:

函数f1(){console.log(this);}函数f2(){console.log(this);}函数f3(){console.log(此);}常数o={f1,f2,[符号.不透明度]:{f2:正确}};f1();//日志`globalThis`。带(o){f1();//日志`o`。f2();//`f2`是不可操作的,因此它记录了`globalThis`。f3();//`f3`不在`o`上,因此记录`globalThis`。}

和:

“使用严格”;函数f(){console.log(this);}f();//日志“未定义”。//严格模式代码中不允许使用`with`语句。

请注意,在评估时,没关系哪里定义了一个正规函数.

.调用,.应用,.绑定,此Arg、和原语

第5步的另一个结果常规CallBindThis,结合步骤6.2(规范中的6.b),是一个原语吗值强制为对象只有在“邋遢”模式下。

为了检查这一点,让我们为value:覆盖绑定:4

  • Function.prototype.apply(thisArg,argArray)
  • 功能.原型。{呼叫,绑定}(thisArg,…args)

.绑定创建一个绑定函数,其绑定设置为这个Arg并且不能再次更改。.调用.应用立即调用函数,使用绑定设置为这个Arg.

.调用.应用直接映射到呼叫,使用指定的这个Arg..绑定使用创建绑定函数绑定函数创建。这些有他们自己的 [[调用]]方法它查找函数对象的[[BoundThis]]内部插槽。

设置自定义的示例值:

函数f(){console.log(this);}常量myObj={},g=f.bind(myObj),h=(m)=>m();//所有这些都记录为“myObj”。g();f.bind(myObj)();f.调用(myObj);h(g);

对于对象,这在严格模式和非严格模式下是相同的。

现在,尝试提供原语值:

函数f(){console.log(this);}const myString=“s”,g=f.bind(myString);g();//日志`String{“s”}`。f.call(myString);//日志`String{“s”}`。

在非限定模式下,原语被强制为对象映射形式对象(“s”)新字符串(“s”).在严格模式下,您可以使用基本体:

“使用严格”;函数f(){console.log(this);}const myString=“s”,g=f.bind(myString);g();//日志“s”。f.call(myString);//日志“s”。

库使用这些方法,例如jQuery将到此处选择的DOM元素:

$(“按钮”).点击(函数(){console.log(this);//记录单击的按钮。});

建造师,、和新的

使用新的操作员,评估新建电话构造,它调用[[Construct]]方法。如果函数是基构造函数(即不是类扩展{}),它设置this参数到从构造函数的原型创建的新对象。属性设置于在构造函数中将结束于结果实例对象。是隐式返回的,除非您明确返回自己的非原始值。

一个是ECMAScript 2015中引入的一种创建构造函数的新方法。

函数旧(a){这.p=a;}const o=新旧(1);控制台.log(o);//日志`Old{p:1}`。类新建{建造师(a){这个。p=a;}}const n=新建(1);控制台.log(n);//日志`New{p:1}`。

类定义隐式位于严格模式:

A级{m1(){返回此;}m2(){常数m1=this.m1;控制台.log(m1());}}新A().m2();//日志“未定义”。

超级的

具有的行为的异常新的类扩展{},如上所述。派生类不会立即设置其调用时的值;只有通过一系列超级的调用(隐式发生,没有自己的建造师). 使用打电话之前超级的不允许。

打电话超级的使用调用的词法范围(函数Environment Record)的值。获取此值有一个特殊的规则超级的电话。它使用绑定ThisValue设置环境记录。

类DerivedNew扩展了New{构造函数(a,a2){//在“super”之前使用“this”会导致ReferenceError。超级(a);this.p2=a2;}}const n2=新派生新(1,2);控制台.log(n2);//日志`DerivedNew{p:1,p2:2}`。

5.评估类字段

ECMAScript 2022中引入了实例字段和静态字段。

被评估,类别定义评估执行,修改运行执行上下文对于每个ClassElement(类元素):

  • 如果字段是静态的,那么指的是类本身,
  • 如果字段不是静态的,那么引用实例。

私人领域(例如。#x个)和方法添加到PrivateEnvironment。

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

注意,在方法和getters/setter中,工作方式与普通函数属性类似。

课堂演示{a=此;b(){返回此;}静态c=此;静态d(){返回此;}//Getter、setter、private修饰符也是可能的。}const demo=新的demo;console.log(demo.a,demo.b());//两个日志都是“demo”。console.log(Demo.c,Demo.d());//两个日志都是“演示”。

1:(离岸价)等于o.f();(f) ()等于f()。这在中进行了解释这篇2岁的文章(已存档). 特别是看如何带圆括号表达式已评估.

2:它必须是成员表达式,不能是属性,必须具有正好为的[[ReferencedName]]“评估”,并且必须是%eval%内部对象。

:规范中规定的任何时候“让裁判是评估的结果X”,然后X(X)是需要找到其计算步骤的某个表达式。例如,评估成员表达式调用表达式是其中之一的结果这些算法。其中一些会导致参考记录.

4:还有其他一些本机和主机方法允许提供价值,尤其是数组原型.map,数组原型.forEach等接受此Arg作为他们的第二个论点。任何人都可以用自己的方法来改变喜欢(func,thisArg)=>函数绑定(thisArg),(func,thisArg)=>函数调用(thisArk)等。一如既往,MDN公司提供了很棒的文档。


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

对于每个代码段,回答以下问题:在标记线?为什么?”.

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

  1. if(真){console.log(this);//这是什么?}

    全球This标记的行在初始全局执行上下文中进行评估。

  2. 常量对象={};函数myFun(){return{//这里的`this`是什么?“是对象”:此===对象,“是全局this”:this===globalThis};}obj.method=我的乐趣;console.log(obj.method());

    对象。将函数作为对象的属性调用时,使用绑定集到基础参考文献的对象方法,即。对象.

  3. 常量对象={myMethod:function(){return{//这里的`this`是什么?“是对象”:此===对象,“是全局this”:this===globalThis};}},myFun=obj.myMethod;console.log(myFun());

    全球This。由于函数值我的乐趣/对象.my方法不是从对象中调用的,作为属性绑定将是全球This.这与Python不同,后者访问方法(obj.my方法)创建一个绑定方法对象.

  4. 常量对象={myFun:()=>({//这是什么?“是对象”:此===对象,“是全局this”:this===globalThis})};console.log(obj.myFun());

    全球This.箭头函数不创建自己的结合。词法范围与初始全局范围相同,因此全球This.

  5. 函数myFun(){console.log(this);//这是什么?}常量对象={myMethod:function(){eval(“myFun()”);}};对象myMethod();

    全球This。在评估直接评估电话时,对象然而,在eval代码中,我的乐趣没有从对象中调用,因此绑定设置为全局对象。

  6. 函数myFun(){//这是什么?返回{“是对象”:此===对象,“是全局this”:this===globalThis};}常量对象={};console.log(myFun.call(obj));

    对象.线路myFun.call(obj);正在调用特殊的内置函数功能.原型.call,接受此Arg作为第一个参数。

  7. 类别MyCl{arrow=()=>(这里的“this”是什么?“是MyCls”:这===MyCls,“is globalThis”:this===全局this,“is instance”:MyCls的这个实例});}console.log(新MyCls().arrow());

    这是一个MyCls公司.箭头函数不会更改绑定,所以它来自词汇范围。因此,这是完全一样与上面提到的类字段一样,比如a=此;。尝试将其更改为静态箭头你得到你期望的结果了吗?

2
  • 2
    另一种常见情况:使用调用EventHandler设置为当前目标事件。未来可以包括这三项提案:绑定运算符::,明确这一点,论点反思.DOM 0事件属性,如onclick(单击)同样值得注意的是:JS代码被隐式包装在一个具有范围文件一个用于单击的元素,引起混乱;是具有该属性的元素。 2021年11月21日7:32
  • @LRDPRDX这个答案包含了但没有人真正需要在全球范围内,具有已弃用,评估不鼓励,应在任何地方使用严格模式等。剩下的就是对象方法()电话方法具有对象作为如果方法是一个功能或方法;func()电话函数没有任何;.绑定,.调用、和.应用可用于绑定明确地;箭头函数没有得到结合。类:在静态事物中,指类本身,在非状态事物中指正在创建的实例。就是这样。 2022年2月8日21:51
190

这个关键字在JavaScript中的行为与其他语言不同。在面向对象语言中关键字引用类的当前实例。在JavaScript中由函数的调用上下文决定(context.function())以及它的名字。

1.在全球范围内使用时

当您使用在全局上下文中,它绑定到全局对象(窗口在浏览器中)

文档.写入(this)//[对象窗口]

当您使用在全局上下文中定义的函数内,仍然绑定到全局对象,因为该函数实际上是全局上下文的方法。

函数f1(){返回此;}document.write(f1())//[对象窗口]

以上f1级是一种全局对象的方法。因此我们也可以调用它窗口对象如下:

函数f(){返回此;}document.write(window.f())//[对象窗口]

2.使用内部对象方法时

当您使用对象方法中的关键字,绑定到“立即数”封闭对象。

var对象={名称:“obj”,f: 函数(){返回this+“:”+this.name;}};document.write(obj.f())//[对象对象]:obj

上面我用双引号将单词immediate括起来。这是为了说明,如果将对象嵌套在另一个对象中,那么绑定到直接父级。

var对象={名称:“obj1”,嵌套bj:{名称:“nestedobj”,f: 函数(){return this+“:”+this.name;}}            }document.write(obj.nestedobj.f())//[对象对象]:nestedobj

即使您将函数显式地作为方法添加到对象中,它仍然遵循上述规则,即仍然指向直接父对象。

变量obj1={名称:“obj1”,}函数returnName(){return this+“:”+this.name;}obj1.f=返回名称//将方法添加到对象document.write(obj1.f())//[对象对象]:obj1

3.调用无上下文函数时

当您使用在没有任何上下文的情况下调用的函数内部(即不在任何对象上),它绑定到全局对象(窗口(即使函数是在对象内部定义的)。

var context=“global”;变量对象={上下文:“对象”,方法:函数(){函数f(){var context=“function”;return this+“:”+this.context;};返回f()//在没有上下文的情况下调用}};document.write(obj.method())//[object Window]:全局

尝试所有功能

我们也可以用函数尝试以上几点。然而,也存在一些差异。

  • 上面我们使用对象文字符号向对象添加了成员。我们可以使用向函数添加成员.以指定它们。
  • 对象文字符号创建了一个可以立即使用的对象实例。对于函数,我们可能需要首先使用新的操作员。
  • 同样在对象文字方法中,我们可以使用点运算符显式地向已定义的对象添加成员。这仅添加到特定实例。然而,我已经在函数原型中添加了变量,以便它能够反映在函数的所有实例中。

下面我尝试了我们用Object和但首先要创建函数,而不是直接编写对象。

/********************************************************************* 1.使用此关键字向函数添加变量时添加到函数原型中,从而允许所有函数实例添加自己的变量副本。*********************************************************************/函数functionDef(){this.name=“对象定义”;this.getName=函数(){return this+“:”+this.name;}}        obj1=新函数Def();document.write(obj1.getName()+“<br/>”)//[对象对象]:对象定义/*********************************************************************2.显式添加到函数protorype的成员也有行为如上所述:所有函数实例都有自己的添加了变量。*********************************************************************/functionDef.prototype.version=1;functionDef.prototype.getVersion=函数(){return“v”+this.version//查看this.version如何引用//通过添加的版本变量//原型}document.write(obj1.getVersion()+“<br/>”)//第1版/********************************************************************* 3.说明以上两者相加的函数变量方法在函数实例之间有自己的副本*********************************************************************/functionDef.prototype.incrementVersion=函数(){this.version=this.version+1;}var obj2=新函数Def();document.write(obj2.getVersion()+“<br/>”)//第1版obj2.incrementVersion()//obj2中的递增版本//不影响obj1版本document.write(obj2.getVersion()+“<br/>”)//第2版document.write(obj1.getVersion()+“<br/>”)//第1版/********************************************************************* 4.`this`关键字表示直接父对象。如果你通过函数原型嵌套对象,然后在其中嵌入this对象是指嵌套对象,而不是函数实例*********************************************************************/functionDef.prototype.nestedObj={名称:'nestedObj',getName1:function(){return this+“:”+this.name;}                            };document.write(obj2.nestedObj.getName1()+“<br/>”)//[对象对象]:nestedObj/********************************************************************* 5.如果方法位于对象的原型链上,则“this”表示对于调用该方法的对象,就好像该方法处于打开状态一样对象。*********************************************************************/var ProtoObj={fun:function(){return this.a}};var obj3=对象创建(ProtoObj)//创建对象设置ProtoObj//作为其原型obj3.a=999//向obj3添加实例成员document.write(obj3.fun()+“<br/>”)//999//调用obj3.fun()使//ProtoObj.fun()以访问obj3.a//如果在obj3上定义了fun()

4.在构造函数函数内部使用时.

当函数用作构造函数时(即使用新的关键字),内部函数体指向正在构造的新对象。

var myname=“全局上下文”;函数SimpleFun(){this.myname=“简单函数”;}var obj1=新SimpleFun()//将myname添加到obj1//1. `new`使SimpleFun()中的`this`指向//对象,从而添加任何成员//使用this.membername在SimipleFun()中创建//正在构造的对象//2. 默认情况下,“new”使函数新返回//构造对象(如果未指定显式返回值)document.write(obj1.myname)//简单函数

5.在原型链上定义的函数内部使用时

如果方法位于对象的原型链上,在这种方法中,是指调用该方法的对象,就好像该方法是在对象上定义的一样。

var ProtoObj={乐趣:函数(){返回this.a;}};//Object.create()创建以ProtoObj为其对象//原型并将其分配给obj3,从而使fun()//作为其原型链上的方法var obj3=对象创建(ProtoObj);obj3.a=999;document.write(obj3.fun())//999//注意,fun()是在obj3的原型上定义的,但是//`this.a`inside-fun()检索obj3.a

6.在call()、apply()和bind()函数内部

  • 所有这些方法都定义于功能.原型.
  • 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定其将在执行该功能时使用。它们还接受在调用原始函数时传递给它的任何参数。
  • fun.apply(obj1[,argsArray])集合对象1作为的值里面fun()和电话fun()传递元素args阵列作为其论据。
  • fun.call(对象1[,参数1[,变量2[,参数3[,…]]])-集合对象1作为的值里面fun()和电话fun()经过arg1、arg2、arg3。。。作为其论据。
  • 乐趣绑定(obj1[,arg1[,arg2[,arg3[,…]]])-返回对函数的引用乐趣具有内部乐趣绑定到对象1和的参数乐趣绑定到指定的参数arg1、arg2、arg3,。。。.
  • 到目前为止应用,呼叫绑定一定很明显。应用允许将参数指定为类似数组的对象,即具有数字的对象长度属性和相应的非负整数属性。鉴于呼叫允许直接指定函数的参数。两者都有应用呼叫在指定的上下文中使用指定的参数立即调用函数。另一方面,绑定只返回绑定到指定值和参数。我们可以通过将此返回函数赋值给变量来捕获它的引用,然后可以随时调用它。
函数加法(inc1,inc2){返回this.a+inc1+inc2;}变量o={a:4};document.write(添加.call(o,5,6)+“<br/>”)//15//上面的add.call(o,5,6)在内部设置了“this”//将()添加到`o`并调用add(),结果是://此.a+inc1+inc2=//`o.a`即4+5+6=15document.write(add.apply(o,[5,6])+“<br/>”)//15//`o.a`即4+5+6=15var g=添加绑定(o,5,6)//g: `o.a`即4+5+6document.write(g()+“<br/>”)//15var h=添加绑定(o,5)//h: ‘o.a’即4+5+?文档.写入(h(6)+“<br/>”)//15// 4 + 5 + 6 = 15document.write(h()+“<br/>”)//NaN公司//没有参数传递给h()//因此add()中的inc2是“未定义的”`//4+5+未定义=NaN

7内部事件处理程序

  • 将函数直接分配给元素的事件处理程序时,使用直接在事件处理函数内部引用相应的元素。这样的直接功能分配可以使用addeventListener(添加侦听器)方法或通过传统的事件注册方法,如onclick(单击).
  • 同样,当您使用直接在event属性内(如<button onclick=“…这…”>)在元素中,它指的是元素。
  • 然而,使用通过事件处理函数或事件属性内调用的其他函数间接解析为全局对象窗口.
  • 当我们使用Microsoft的事件注册模型方法将函数附加到事件处理程序时,也可以实现上述相同的行为附件事件。它没有将函数分配给事件处理程序(从而生成元素的函数方法),而是对事件调用函数(有效地在全局上下文中调用它)。

我建议最好试试这个J小提琴.

<脚本>函数clickedMe(){警报(this+“:”+this.tagName+“:“+this.id);} document.getElementById(“button1”).addEventListener(“click”,clickedMe,false);document.getElementById(“button2”).onclick=单击我;document.getElementById(“button5”).attachEvent(“单击”,单击我);</script><h3>在事件处理程序或事件属性中“直接”使用`this`</h3><button id=“button1”>使用addEventListner()单击“assigned”()<button id=“button2”>click()使用click()“分配”</button><br/><button id=“button3”onclick=“alert(this+':'+this.tagName+':'+this.id);”>直接在单击事件属性中使用了`this`<h3>在事件处理程序或事件属性中“间接”使用`this`</h3><button onclick=“alert((function(){return this+':'+this.tagName+':'+this.id;})());”>`此`在函数内部间接使用<br/>定义并调用了内部事件属性</button><button id=“button4”onclick=“clickedMe()”>`这`是间接使用的,内部函数调用了内部事件属性仅限IE:<button id=“button5”>使用attachEvent()单击()“attached”

8在ES6箭头功能中

在箭头函数中,将表现为普通变量:它将从其词法范围继承。函数的,其中定义了箭头函数,将是箭头函数的.

因此,这与以下行为相同:

(function(){}).bind(this)

请参阅以下代码:

const globalArrowFunction=()=>{返回此;};console.log(globalArrowFunction())//窗口const上下文对象={方法1:()=>{return this},方法2:function(){return()=>{returnthis};}};console.log(contextObject.method1())//窗口const contextLessFunction=contextObject.method1;console.log(contextLesFunction())//窗口console.log(contextObject.method2()())//上下文对象const innerArrowFunction=contextObject.method2();console.log(innerArrowFunction())//contextObject(上下文对象)
0
77
+500

Javascript的

简单函数调用

考虑以下功能:

函数foo(){console.log(“bar”);console.log(this);}foo();//调用函数

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

在浏览器中运行时将被记录为窗口。这是因为窗口是web浏览器作用域中的全局变量。

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

现在,如果我们通过添加以下语句在严格模式下运行“使用严格”;到函数声明的开头,将不再引用任一环境中的全局变量。这样做是为了避免在严格模式下出现混淆。在这种情况下会记录未定义,因为这就是它的本质,所以它没有定义。

在以下情况中,我们将了解如何操作.

对对象调用函数

有不同的方法可以做到这一点。如果您在Javascript中调用了本机方法,如对于每个,您应该已经知道在这种情况下,变量是指对象调用该函数的位置(请注意,在javascript中,几乎所有内容都是对象,包括阵列s和功能s) ●●●●。以下面的代码为例。

var myObj={key:“Obj”};myObj.logThis=函数(){//我是一种方法console.log(this);}myObj.logThis();//myObj已被记录

如果对象包含一个包含功能,该属性称为方法。此方法在调用时将始终具有变量设置为对象它与关联。这对严格和非严格模式都适用。

注意,如果一个方法存储(或者说复制)在另一个变量中不再保留在新变量中。例如:

//继续前面的代码片段var myVar=myObj.logThis;myVar();//根据操作模式记录window/global/undefined

考虑一个更常见的实际场景:

var el=文档.getElementById('idOfEl');el.addEventListener('click',function(){console.log(this)});//addEventListener调用的函数将此作为元素的引用//所以点击元素就会记录该元素本身

这个新的关键字

考虑一下Javascript中的构造函数:

职能人员(姓名){this.name=名称;this.sayHello=函数(){console.log(“Hello”,this);}}var awal=新人员(“awal”);awal.sayHello();//在`awal.sayHello`中,`this`包含对变量`awal的引用`

这是如何工作的?好吧,让我们看看当我们使用新的关键字。

  1. 使用调用函数新的关键字将立即初始化对象类型为.
  2. 此的构造函数对象将其构造函数设置为此外,请注意水洼类型会回来的对象只有。
  3. 这个新的对象将分配给人员.原型。这意味着原型将可用于以下所有实例,包括阿瓦尔.
  4. 功能自身现在被调用;是对新构建对象的引用阿瓦尔.

很简单,嗯?

请注意,官方的ECMAScript规范中没有任何地方声明这种类型的函数是实际的建造师功能。它们只是正常的功能,并且新的可用于任何函数。只是我们这样使用它们,所以我们只把它们称为这样。

在函数上调用函数:呼叫应用

所以是的,因为功能s也是物体(实际上是Javascript中的第一类变量),甚至函数也有。。。好吧,功能本身。

所有函数都继承自全局功能,并且它的许多方法中有两种是呼叫应用,两者都可以用于操作在调用它们的函数中。

函数foo(){console.log(this,arguments);}var thisArg={myObj:“很酷”};foo.call(thisArg,1,2,3);

这是使用呼叫。它基本上取第一个参数并设置在函数中foo公司作为参考这个Arg。传递给的所有其他参数呼叫传递给函数foo公司作为参数。
因此上述代码将记录{myObj:“很酷”},[1,2,3]在控制台中。更改在任何功能中。

应用几乎与呼叫接受它只需要两个参数:这个Arg以及一个数组,其中包含要传递给函数的参数。所以上面的呼叫调用可以转换为应用像这样:

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

请注意呼叫应用可以覆盖的值我们在第二个项目符号中讨论的逐点设置方法调用。足够简单:)

呈现。。。。绑定!

绑定是的兄弟呼叫应用。它也是由全局功能Javascript中的构造函数。两者之间的差异绑定呼叫/应用两者都是吗呼叫应用将实际调用该函数。绑定,返回一个新函数这个Arg论据预设。让我们举一个例子来更好地理解这一点:

函数foo(a,b){console.log(this,参数);}var thisArg={myObj:“现在更酷了”};var绑定=foo.bind(thisArg,1,2);console.log(绑定类型);//日志`函数`console.log(绑定);/*日志`函数(){本机代码}`*/bound();//调用`.bind返回的函数`//logs`{myObj:“现在更酷了”},[1,2]`

看到三者之间的区别了吗?这是微妙的,但它们的用法不同。喜欢呼叫应用,绑定也会超过通过点方法调用设置。

还要注意,这三个函数都不会对原始函数进行任何更改。呼叫应用将从新构造的函数返回值,而绑定将返回新构造的函数本身,以便调用。

额外的东西,复制这个

有时,你不喜欢这样的事实更改范围,尤其是嵌套范围。看看下面的例子。

var myObj={您好:函数(){返回“世界”},myMethod:函数(){//复制这个,变量名是区分大小写的var that=这个;//回调ftw\o/foo.bar(“args”,函数(){//我想在这里打“你好”this.hello();//错误//但是“this”指的是“foo”该死的!//哦,等等,我们有备份/那个.hello();//“世界”});}};

在上面的代码中,我们看到更改了嵌套范围,但我们需要从原来的范围。所以我们“复制”了那个并使用副本而不是.聪明,嗯?

索引:

  1. 保存在中的内容默认情况下?
  2. 如果我们用Object-dot符号将函数作为方法调用会怎么样?
  3. 如果我们使用新的关键字?
  4. 我们如何操作具有呼叫应用?
  5. 使用绑定.
  6. 正在复制解决嵌套范围问题。
0
49

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

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

(函数(值){this.value=值;$('.some-elements').each(函数(elt){elt.innerHTML=this.value;//哦!!可能未定义});})(2);

所以这里,在each()中,“this”并不包含您期望的“value”(来自

this.value=值;
上方)。因此,为了克服这个问题(不是双关语),开发人员可以:

(函数(值){var self=此;//零钱self.value=值;$('.some-elements').each(函数(elt){elt.innerHTML=self.value;//呸!!==2});})(2);

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

7
  • 6
    “JS中的一切都是对象”不是真的,JavaScript也有原语值,请参阅bclary.com/2004/11/07/#a-4.3.2 2010年6月27日14:47
  • 6
    基本值本身似乎有一些方法,如String#substring()、Number#toString()等。因此,可能与那篇文章的命名不同,它们的行为实际上就像是对象一样(它们都是原型,即String#substring()实际上是:String.prototype.substring=function(){…})。如果我错了,请纠正我。 2010年7月4日16:49
  • 12
    这个关键字与范围无关。此外,它在不是对象属性的函数中也有意义。
    – 贝吉
    2012年12月8日20:59
  • 1
    @阿伦吉辛格——关于这一点,有两个学派。我喜欢说“一切都是一个对象,但为了方便起见,有些可以用原语表示". ;-)
    – 罗布·G
    2015年1月6日23:20
  • 9
    并不是全部关于范围。这一切都与执行上下文有关,这与范围不同。JavaScript在词汇上有范围(意思是范围由代码的位置决定),但由包含它的函数的调用方式决定,而不是由该函数所在的位置决定。 2016年3月16日5:00
20

自从这个帖子出现后,我为新读者收集了一些要点主题。

的价值如何决心?

我们使用这个类似于我们在英语等自然语言中使用代词的方式:“约翰跑得很快,因为正在努力赶上火车。”相反,我们可以写下“…约翰正在努力赶火车”。

var人={名字:“佩内洛普”,lastName:“巴里摩尔”,fullName:函数(){//我们使用“this”就像上面的句子一样:console.log(this.firstName+“”+this.lastName);//我们还可以写:console.log(person.firstName+“”+person.lastName);}}

未赋值直到对象调用定义它的函数。在全局范围内,所有全局变量和函数都定义在窗口对象。因此,在全局函数中,是指全局窗口对象。

什么时候?使用严格,在全局函数和未绑定到任何对象的匿名函数中,值为未定义.

这个关键字为最容易被误解当:1)我们借用了一种方法,2)我们指定了一个使用变量,3)使用作为回调函数传递,4)在闭包内部使用-内部函数。(2)

桌子

什么决定了未来

定义于ECMA脚本6,箭头功能采用从绑定封闭(函数或全局)范围。

函数foo(){//返回箭头函数返回(a)=>{//`this`这里是从`foo()词汇继承而来的`console.log(this.a);};}变量obj1={a:2};变量obj2={a:3};var bar=foo.call(obj1);酒吧呼叫(obj2);//2,不是3!

虽然箭头函数提供了使用绑定(),需要注意的是,它们本质上是在禁用传统有利于更广泛理解词汇范围的机制。(1)


参考文献:

  1. 此对象原型(&O)作者:凯尔·辛普森。©2014 Getify Solutions版权所有。
  2. javascriptissexy.com-网址:http://goo.gl/pvl0GX
  3. 安格斯·克罗尔-网址:http://goo.gl/Z2RacU
18

在JavaScript中总是指正在执行.

如果没有定义明确的所有者,则会引用最上面的所有者,即窗口对象。

所以如果我这么做了

函数someKindOfFunction(){this.style='foo';}

element.onclick=someKindOfFunction;

将引用元素对象。但要小心,很多人都会犯这个错误。

<element onclick=“someKindOfFunction()”>

在后一种情况下,您只需引用函数,而不是将其交给元素。因此,将引用窗口对象。

15

执行上下文在javascript中有一个参数设置方式:

  1. 如何调用函数(包括作为对象方法,使用呼叫应用,使用新的)
  2. 的使用绑定
  3. 箭头功能的词汇(它们采用外部执行上下文)
  4. 代码是处于严格模式还是非严格模式
  5. 是否使用调用了代码评估

您可以设置的值使用函数调用,功能应用程序函数.bind.

默认情况下,当在DOM元素上引发事件后调用侦听器时函数的值是DOM元素。

使用jQuery.proxy,jQuery可以轻松地进行更改。

7
  • 9
    说每个函数呼叫具有作用域。换句话说,令人困惑的是在Javascript中是函数本身的固有属性,而不是函数调用方式的工件。
    – 波蒂
    2010年6月27日14:34
  • @非常感谢。在js中,最令人困惑的是,在前面使用的所有语言(c、c++)中,这是无法操作的,n始终指向对象实例,而在js里,这取决于并可以在使用函数.call,函数绑定等–苏希尔
    – 苏希尔
    2013年6月25日10:04
  • 2
    引用函数的范围。将引用特定对象(或可能未定义),正如您所说,可以使用更改.call().apply().A函数的范围本质上是(简化后)它可以访问哪些变量,这完全取决于函数的声明位置,并且不能更改。
    – nnnnnn
    2015年1月3日22:48
  • @Pointy(点):“说每个函数调用都有一个作用域更为正确。”更正确的说法是函数(和现在的块)具有范围,函数电话上下文.Scope定义了该范围内的代码可以使用的标识符。上下文定义了这些标识符绑定到的内容。 2015年11月14日15:09
  • 1
    “无论该范围是什么,都由”this“引用。”不,在ES5和之前(例如,在编写此答案时),范围与彼此无关。在ES2015(又名ES6)中,和范围相关相当小的wrt箭头函数(中的箭头函数继承自其封闭范围),但从不引用范围。 2015年11月14日15:09
11

大牛,太棒了!这上面有几个词,还有一个很好的列表事件处理程序中的执行上下文指针。

总之,在JavaScript中,指向运行当前函数的对象(或从其执行上下文),并且它始终是只读的,无论如何都无法设置它(这样的尝试将以“Invalid left-side in assignment”消息结束。

对于事件处理程序:内联事件处理程序,例如<element onclick=“foo”>,重写之前附加的任何其他处理程序,因此要小心,最好不要使用内联事件委托。感谢Zara Alaverdyan,她通过一场反对的辩论激励我列出了这个例子:)

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

在这里是一个很好的来源在里面JavaScript脚本.

总结如下:

  • 全球这个

    在浏览器中,在全局范围内,窗口对象

    <script type=“text/javascript”>console.log(这个===窗口);//真的var foo=“bar”;console.log(this.foo);//“酒吧”console.log(window.foo);//“酒吧”

    节点使用repl,是顶级命名空间。您可以将其称为全球的.

    >这个{ArrayBuffer:[函数:ArrayBuffer],Int8Array:{[函数:Int8Array]BYTES_PER_ELEMENT:1},Uint8Array:{[函数:Uint8Array]BYTES_PER_ELEMENT:1},...>全局===此真的

    节点从脚本执行,在全局范围开始时是一个空对象。它与全球的

    \\测试.jsconsole.log(this);\\{}console.log(这===全局);\\法斯莱
  • 函数this

DOM事件处理程序或这个Arg在节点和浏览器中使用在未使用调用的函数中新的引用全局范围…

<script type=“text/javascript”>foo=“bar”;函数testThis(){this.foo=“foo”;}console.log(this.foo)//日志“栏”testThis();console.log(this.foo)//日志“foo”</script>

如果您使用使用严格;,在这种情况下未定义

<script type=“text/javascript”>foo=“bar”;函数testThis(){“使用严格”;this.foo=“foo”;}console.log(this.foo)//日志“栏”testThis()//Uncaught TypeError:无法设置未定义的属性“foo”</脚本>

如果使用调用函数新的这个将是一个新的上下文,它不会引用全局.

<script type=“text/javascript”>foo=“bar”;函数testThis(){this.foo=“foo”;}console.log(this.foo)//日志“栏”新测试This();console.log(this.foo)//日志“栏”console.log(新testThis().foo)//日志“foo”</script>
  • 这个原型

您创建的函数将成为函数对象。他们会自动得到一份特别的原型属性,这是您可以为其赋值的对象。当您通过使用调用函数来创建实例时新的您可以访问分配给原型属性。您可以使用以下命令访问这些值.

函数Thing(){console.log(this.foo);}Thing.prototype.foo=“bar”;var thing=新事物()//日志“栏”console.log(thing.foo)//日志“栏”

分配通常是错误的阵列物体原型。如果希望每个实例都有自己的数组,请在函数中创建它们,而不是在原型中创建它们。

函数Thing(){this.things=[];}var thing1=新事物();var thing2=新事物();thing1.things.push(“foo”);console.log(thing1.things)//日志[“foo”]console.log(thing2.things)//日志[]
  • 反对这个

你可以使用在对象的任何函数中引用该对象的其他属性。这与使用创建的实例不同新的.

var对象={foo:“bar”,logFoo:函数(){console.log(this.foo);}};obj.logFoo()//日志“栏”
  • DOM事件this

在HTML DOM事件处理程序中,始终是对事件附加到的DOM元素的引用

函数Listener(){document.getElementById(“foo”).addEventListener(“点击”,this.handleClick);}Listener.prototype.handleClick=函数(事件){console.log(this)//日志“<div id=”foo“></div>”}var listener=新监听器();document.getElementById(“foo”).click();

除非你绑定上下文

函数监听器(){document.getElementById(“foo”).addEventListener(“点击”,this.handleClick.bind(this));}Listener.prototype.handleClick=函数(事件){console.log(this)//日志侦听器{handleClick:function}}var listener=新监听器();document.getElementById(“foo”).click();
  • HTML此

在HTML属性中可以放置JavaScript,是对元素的引用。

<div id=“foo”onclick=“console.log(this);”></div><script type=“text/javascript”>document.getElementById(“foo”).click()//日志<div id=“foo”。。。</脚本>
  • 评估这个

你可以使用评估访问.

函数Thing(){}Thing.prototype.foo=“bar”;Thing.prototype.logFoo=函数(){eval(“console.log(this.foo)”)//日志“栏”}var thing=新事物();thing.logFoo();
  • 用这个

你可以使用具有添加读取和写入当前作用域上的值没有提及明确地。

函数Thing(){}Thing.prototype.foo=“bar”;Thing.prototype.logFoo=函数(){用(这个){console.log(foo);foo=“foo”;}}var thing=新事物();thing.logFoo();//日志“栏”console.log(thing.foo);//日志“foo”
  • j查询这个

jQuery在许多地方都有引用DOM元素。

<div class=“foo bar1”></div><div class=“foo bar2”><script type=“text/javascript”>$(“.foo”).each(函数(){console.log(this)//logs<div class=“foo。。。});$(“.foo”).on(“点击”,函数(){console.log(this)//logs<div class=“foo。。。});$(“.foo”).each(函数(){this.click();});</script>
9

关于如何“这个”关键字是用JavaScript解释的。希望本文能让所有这些一劳永逸。还有更多。请仔细阅读整篇文章。提前警告,这篇文章很长。

无论使用环境如何,“这个”始终引用“当前对象”在Javascript中。然而“当前对象”根据不同而不同上下文. The上下文可能正是第1页,共6页以下内容:

  1. 全球的(即所有功能之外)
  2. 内部直接“非绑定函数”调用(即具有还没有受呼叫约束函数名称.bind)
  3. 内部间接“非绑定函数”调用通过函数名称.callfunctionName.应用
  4. 内部“绑定函数”调用(即一个函数已绑定通过呼叫函数名称.bind)
  5. 通过“新建”创建对象时
  6. 内联DOM事件处理程序内部

以下逐一描述了每一个上下文:

  1. 全球背景(即所有功能之外):

    在所有功能之外(即在全球范围内)“当前对象“(因此“这个”)总是“窗口”对象。

  2. 内部直接“非绑定函数”调用:

    在直接“非绑定函数”调用中,对象被调用的函数调用成为“当前对象”(因此的价值“这个”). 如果调用函数时没有显式当前对象,的当前对象要么是“窗口”对象(对于非严格模式)或未定义(对于严格模式)。中定义的任何函数(或变量)全球背景自动成为的属性“窗口”对象。例如,假设函数在全局上下文中定义为

    函数UserDefinedFunction(){警报(this)}

    它成为窗口对象的属性,就像您定义了它是作为

    窗口。UserDefinedFunction=函数(){警报(this)}

    在“非严格模式”中,直接通过“用户定义函数()”将自动调用它是作为“window.UserDefinedFunction()”制作“窗口”作为“当前对象”(因此“这个”)在“用户定义的函数”。在“非严格模式”下调用此函数将导致以下结果

    UserDefinedFunction()//显示[object Window],因为它会自动作为窗口调用。用户定义的函数()

    在“严格模式”中,直接通过“用户定义函数()”“不”自动调用“window.UserDefinedFunction()”。因此“当前对象“(以及“这个”)在“用户定义函数”应为未定义。在“严格模式”下调用此函数将导致以下结果

    UserDefinedFunction()//显示未定义

    然而,使用窗口对象显式调用它将导致以下内容

    窗口。UserDefinedFunction()//“无论模式如何,始终显示[object Window]。”

    让我们看另一个例子。请查看以下代码

    函数UserDefinedFunction(){警告(this.a+“,”+this.b+“,“+this.c+”,“+this.d)}变量o1={a: 1、,b: 2、,f: 用户定义函数}无功氧气={c: 3、,d: 4、,f: 用户定义函数}o1.f()//应显示1,2,未定义,未定义o2.f()//应显示未定义、未定义、3,4

    在上面的例子中,我们看到,当“用户定义函数”通过调用o1号机组,“这个”取值为o1号机组其属性的值“a”“b”显示。价值观属于“c”“d”显示为未定义作为o1号机组不定义这些属性

    类似地,当“用户定义函数”通过调用氧气,“这个”取值为氧气及其属性的价值“c”“d”显示。的价值“a”“b”显示为未定义作为氧气没有定义这些属性。

  3. 内部间接“非绑定函数”调用通过函数名称.callfunctionName.应用:

    “非绑定函数”通过调用函数名称.callfunctionName.应用,的“当前对象”(因此“这个”)设置为的值“这个”传递给的参数(第一个参数)调用/应用。下面的代码也进行了演示。

    函数UserDefinedFunction(){警报(this.a+”、“+this.b+”、“+this.c+”、“+this.d)}变量o1={a: 1、,b: 2的情况下,f: 用户定义函数}无功氧气={c: 3、,d: 4、,f: 用户定义函数}UserDefinedFunction.call(o1)//应显示1,2,undefined,undefineUserDefinedFunction.apply(o1)//应显示1,2,undefined,undefineUserDefinedFunction.call(o2)//应显示未定义、未定义、3,4UserDefinedFunction.apply(o2)//应显示未定义、未定义、3,4o1.f.call(o2)//应显示未定义、未定义、3,4o1.f.应用(o2)//应显示未定义、未定义、3,4o2.f.call(o1)//应显示1,2,未定义,未定义o2.f.apply(o1)//应显示1,2、未定义、未定义

    上面的代码清楚地显示了任何“NON”的“this”值绑定函数”可以通过调用/应用此外,如果“这个”参数未显式传递给调用/应用,“当前对象”(因此“this”的值)设置为“窗口”非严格模式和“未定义”在严格模式下。

  4. 内部“绑定函数”调用(即通过调用绑定的函数函数名称.bind):

    绑定函数是其“这个”值已被固定的。下面的代码演示了如何“这个”在情况下工作绑定函数的

    函数UserDefinedFunction(){警报(this.a+”、“+this.b+”、“+this.c+”、“+this.d)}变量o1={a: 1、,b: 2、,f: 用户定义函数,bf:空}无功氧气={c: 3、,d: 4、,f: 用户定义函数,bf:空}var bound1=用户定义函数绑定(o1);//将函数“bound1”的“this”值永久修复为对象o1bound1()//应显示1,2,未定义,未定义var bound2=UserDefinedFunction.bind(o2);//将函数“bound2”的“this”值永久修复为对象o2bound2()//应显示未定义、未定义、3,4var bound3=o1.f.bind(o2);//将函数“bound3”的“this”值永久修复为对象o2bound3()//应显示未定义、未定义、3,4var bound4=o2.f.bind(o1);//将函数“bound4”的“this”值永久修复为对象o1bound4()//应显示1,2,undefined,undefineo1.bf=UserDefinedFunction.bind(o2)//永久性地将函数“o1.bf”的“this”值修复为对象o2o1.bf()//应显示未定义、未定义、3,4o2.bf=UserDefinedFunction.bind(o1)//永久地将函数“o2.bf”的“this”值修复到对象o1o2.bf()//应显示1,2,未定义,未定义bound1.call(o2)//仍应显示1,2,undefined,undefine。“call”无法更改绑定函数的“this”值bound1.apply(o2)//仍应显示1,2,undefined,undefine。“apply”无法更改绑定函数的“this”值o2.bf.call(o2)//仍应显示1,2,undefined,undefine。“call”无法更改绑定函数的“this”值o2.bf.apply(o2)//仍应显示1,2,未定义,未定义。“apply”无法更改绑定函数的“this”值

    如以上代码中所给出的,任何“绑定函数”的“this”值无法通过调用/应用更改此外,如果“这个”参数未显式传递给绑定,“当前对象”(因此“这个”)设置为“窗口”在非严格模式和“未定义”严格模式下。还有一件事。绑定已绑定的函数不会更改“这个”.它仍然设置为第一个绑定函数设置的值。

  5. 通过“新建”创建对象时:

    在构造函数中“当前对象”(因此“这个”)引用当前正在创建的对象通过“新”无论函数的绑定状态如何。然而如果构造函数是绑定函数,则应使用调用为绑定函数设置的预定义参数集。

  6. 内联DOM事件处理程序内部:

    请查看以下HTML代码段

    Hello World你好世界<div style='宽度:100px;高度:100px;'onclick='OnDivClick(event,this)'>你好世界

    这个“这个”在上述示例中,请参考“button”元素和“div”元素。

    在第一个示例中,按钮的字体颜色应设置为单击时为白色。

    在第二个示例中,当“div”(div)元素被点击,它应该呼叫OnDivClick上函数及其第二个参数引用单击的div元素。然而“这个”在OnDivClick中不得引用单击的分区元素。应设置为“窗口对象”“未定义”在里面非严格要求严格模式分别(如果OnDivClick上是一个未绑定函数)或设置为预定义绑定值(如果OnDivClick上是一个有界函数)

下面总结了整篇文章

  1. 在全球范围内“这个”总是指“窗口”对象

  2. 无论何时调用函数,都会在对象(“当前对象”). 如果当前对象未明确提供,这个当前对象“窗口对象”在里面非严格模式“未定义”默认情况下为严格模式。

  3. 的价值“这个”在非绑定函数中是对调用函数的上下文中的对象的引用(“当前对象”)

  4. 的价值“这个”Non-Bound函数中的呼叫应用函数的方法。

  5. 的价值“这个”对于Bound函数是固定的,不能被覆盖呼叫应用函数的方法。

  6. 绑定和已绑定的函数不会更改“this”的值。它仍被设置为第一个绑定函数设置的值。

  7. 的价值“这个”构造函数中的对象创建并初始化

  8. 的价值“这个”内联DOM事件处理程序内是引用为其提供事件处理程序的元素。

9

可能是关于如下所示:

JavaScript中“this”关键字的温和解释

背后的想法是为了了解函数调用类型对设置的重要性值。


当无法识别时,不要问问自己:

在哪里取自?

但是问问自己:

功能如何已调用?

对于箭头函数(上下文透明度的特殊情况),请自问:

有什么价值其中箭头功能是定义?

这种心态是正确的并且可以让你免于头痛。

1
  • 除了链接到你的博客之外,也许你还可以更深入地研究问这些问题如何帮助别人理解关键字? 2019年6月2日15:06
7

这是我见过的最好的解释:了解JavaScript清晰明了

这个引用总是指object—一个单数对象,通常在函数或方法,尽管它可以在全局范围。请注意,当我们使用strict模式时,它保存的值为在全局函数和未定义的匿名函数中未定义绑定到任何对象。

有四种情况,其中可能会令人困惑:

  1. 当我们传递一个方法(它使用)用作回调函数的参数。
  2. 当我们使用内部函数(闭包)时。注意闭包不能访问外部函数的变量,因为this变量只能由函数本身访问,而不能由内部函数访问。
  3. 当一种方法依赖于跨上下文分配给变量,在这种情况下引用了与最初预期不同的另一个对象。
  4. 使用时以及绑定、应用和调用方法。

他给出了代码示例、解释和解决方案,我认为这非常有用。

6

是JavaScript中被误解的概念之一,因为它的行为在不同的地方几乎没有什么不同。简单地说,指的是我们当前正在执行的函数的“所有者”.

有助于获取我们使用的当前对象(也称为执行上下文)。如果您了解当前函数将在哪个对象中执行,则可以轻松了解当前

var val=“window.val”var对象={val:“obj.val”,innerMethod:函数(){var val=“obj.val.inner”,func=函数(){var self=此;返回self.val;};返回函数;},outerMethod:function(){返回this.val;}};//这实际上是在窗口对象内部执行的console.log(obj.innerMethod()())//返回window.val//分解为2行详细解释了这一点var_inn=对象内部方法();控制台.log(_inn())//返回window.valconsole.log(obj.outerMethod())//返回obj.val

上面我们创建了3个同名的变量“val”。一个在全局上下文中,另一个在obj的innerMethod中。JavaScript通过从局部go-global向上作用域链来解析特定上下文中的标识符。


很少有地方可以区分

调用对象的方法

var状态=1;var辅助程序={状态:2,getStatus:函数(){返回this.status;}};var theStatus1=助手.getStatus()//第1行console.log(状态1)//2var theStatus2=helper.getStatus;console.log(状态2())//1

执行第1行时,JavaScript为函数调用建立执行上下文(EC),设置对象引用的对象位于最后一个“”之前所以在最后一行你可以理解a()在全局上下文中执行,该上下文是窗口.

与构造函数一起

可用于引用正在创建的对象

职能人员(姓名){this.personName=名称;this.sayHello=函数(){return“Hello”+this.personName;}}var person1=新人员(“Scott”);console.log(person1.sayHello())//你好,斯科特var person2=新人员(“Hugh”);var sayHelloP2=person2.sayHello;console.log(sayHelloP2())//Hello未定义

新增时人员()执行时,将创建一个全新的对象。被调用,其设置为引用该新对象。

函数调用

函数testFunc(){this.name=“名称”;this.myCustomAttribute=“自定义属性”;返回此;}var whatIsThis=testFunc();控制台.log(whatIsThis)//窗口var whatIsThis2=新的testFunc();控制台.log(whatIsThis2)//testFunc()/objectconsole.log(window.myCustomAttribute)//自定义属性

如果我们错过了新的关键字,这是什么引用它能找到的最全局上下文(窗口)

使用事件处理程序

如果事件处理程序是内联的,指全局对象

<script type=“application/javascript”>函数click_handler(){警告(this);//警告窗口对象}</script>单击我</按钮>

通过JavaScript添加事件处理程序时,引用生成事件的DOM元素。


6

“this”的值取决于执行函数的“上下文”。上下文可以是任何对象或全局对象,即窗口。

因此,“this”的语义不同于传统的OOP语言。它会带来问题:1.将函数传递给另一个变量时(很可能是回调);和2。从类的成员方法调用闭包时。

在这两种情况下,都设置为window。

6

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

功能车(品牌、型号、年份){var this={};//可以说,在引擎盖下this.make=品牌;this.model=模型;this.year=年;返回此;//发动机罩下面}var mycar=新车(‘Eagle’,‘Talon TSi’,1993);//=====发动机罩下变量this={};this.make=“鹰”;this.model=“泰龙TSi”;this.year=1993年;返回此;
4

沃尔德帮助?(javascript中“this”的大部分混淆是因为它通常不链接到您的对象,而是链接到当前的执行范围——这可能不是它的确切工作方式,但对我来说总是这样——请参阅本文以获得完整的解释)

1
  • 2
    最好说它是有联系的”到当前执行上下文“。除了ES6(草案)使用箭头函数更改了该选项外,该选项在外部执行上下文中解决。
    – 罗布·G
    2015年1月6日23:29

关于的一些信息关键字

让我们记录全局范围内控制台的关键字,没有更多代码,但

console.log(this)

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

console.log(this===窗口)//真

服务器/节点/Javascript运行时 关键字也是一个全局对象,它是模块.导出

console.log(this===module.exports)//真console.log(this===导出)//true

记住出口只是对的引用模块.导出

我有不同的看法我希望其他答案对我有帮助。

查看JavaScript的一种方法是查看只有一种方法可以调用函数1。它是

functionObject.call(objectForThis,arg0,arg1,arg2,…);

始终为提供一些值此对象.

其他一切都是语法糖函数Object.call

所以,其他一切都可以通过它如何转化为函数对象.call.

如果你只是调用一个函数是“全局对象”,在浏览器中是窗口

函数foo(){console.log(this);}foo();//这是窗口对象

换句话说,

foo();

有效地转化为

foo.call(窗口);

请注意,如果使用严格模式,则未定义

'使用严格';函数foo(){console.log(this);}foo();//这是窗口对象

这意味着

换句话说,

foo();

有效地转化为

foo.call(未定义);

在JavaScript中,有这样的运算符+-*还有一个点运算符,它是.

这个.运算符与右边的函数和左边的对象一起使用时,有效地表示“将对象作为功能。

例子

常数bar={name:'bar',foo(){console.log(this);},};bar.foo();//这是酒吧

换句话说bar.foo()翻译成恒定温度=bar.foo;临时调用(bar);

注意,函数是如何创建的并不重要(主要是……)。所有这些都会产生相同的结果

常数bar={name:'bar',fn1(){console.log(this);},fn2:function(){console.log(this);},fn3:其他功能,};函数otherFunction(){console.log(this)};bar.fn1();//这是酒吧bar.fn2();//这是酒吧bar.fn3();//这是酒吧

这些都只是语法糖

{恒定温度=bar.fn1;温度调用(bar);}{恒定温度=bar.fn2;温度调用(bar);}{恒定温度=bar.fn3;温度调用(bar);}

另一个缺陷是原型链。当您使用a.b公司JavaScript首先查看直接由引用的对象对于该物业b条.如果b条在对象上找不到,则JavaScript将在对象的原型中查找b条.

有多种方法可以定义对象的原型,2019年最常见的是关键字。为了尽管这并不重要。重要的是,当它在物体中看起来时用于属性b条如果找到财产b条在对象上或其原型链中,如果b条最终成为一个函数,然后应用上述规则。功能b条引用将使用呼叫方法和传递as objectForThis如此答案顶部所示。

现在。假设我们创建了一个显式设置在调用另一个函数之前,使用.(点)运算符

函数foo(){console.log(this);}功能栏(){const对象ForThis={名称:'moo'}foo.call(objectForThis);//显式传递objectForThis}常量对象={巴,};对象栏();

按照翻译使用呼叫,对象栏()成为恒定温度=obj.bar;临时调用(obj);。当我们进入酒吧我们调用的函数foo公司但是我们显式地为objectForThis传递了另一个对象,所以当我们到达foo时就是那个内在的物体。

这两者都是绑定=>函数有效地做到了。它们更像是语法糖。他们有效地构建了一个新的不可见函数,就像酒吧在上面显式设置在它调用指定的任何函数之前。在绑定的情况下设置为您传递的任何内容绑定.

函数foo(){console.log(this);}const-bar=foo.bind({name:'moo'});//bind创建了一个新的不可见函数,该函数使用绑定的对象调用foo。bar();//此处传递给bar的objectForThis被忽略,因为//bind创建的不可见函数将使用调用foo//我们在上面绑定的对象bar.call({name:其他});

注意,如果函数Object.bind不存在,我们可以这样做

函数绑定(fn,objectForThis){返回函数(…args){return fn.call(objectForthis,…args);};}

然后我们可以这样称呼它

函数foo(){console.log(this);}常量栏=绑定(foo,{name:'abc'});

箭头函数=>运算符是绑定的语法糖

consta=()=>{console.log(this)};

与相同

consttempFn=function(){console.log(this)};const a=tempFn.bind(this);

就像这样绑定,将创建一个新的不可见函数,该函数使用的绑定值调用给定函数此对象但不像绑定要绑定的对象是隐式的。不管怎样碰巧是在=>使用运算符。

所以,就像上面的规则一样

consta=()=>{console.log(this);}//这是全局对象
'使用严格';consta=()=>{console.log(this);}//这是未定义的
函数foo(){return()=>{console.log(this);}}常量对象={foo,};常量b=obj.foo();b();

对象.foo()转换为恒定温度=obj.foo;临时调用(obj);也就是说里面的箭头操作符foo公司将绑定对象到一个新的不可见函数,并返回分配给的新的不可见函数b条.b()将一如既往地发挥作用b.call(窗口)b.call(未定义)调用新的不可见函数foo公司创建。该不可见函数忽略进入并通过对象as objectForThis`指向箭头函数。

上面的代码转换为

函数foo(){函数tempFn(){console.log(this);}返回tempFn.bind(this);}常量对象={foo、,};常量b=obj.foo();b.调用(如果是严格模式,则为窗口或未定义);

1应用是另一个类似于呼叫

functionName.apply(objectForThis,arrayOfArgs);

但从概念上讲,从ES6开始,您甚至可以将其转换为

functionName.call(objectForThis,…arrayOfArgs);
1
  • 老兄,你的解释很有效。完全消除了我的困惑。 2021年3月3日7:42

JavaScript中的“this”这是执行上下文的属性之一。在此处输入图像描述

  • 每次执行函数时都会创建此属性,而不是在此之前。
  • 它的值不是静态的,而是取决于它的使用方式。
  • 采用一个值,该值指向其所在函数的所有者习惯于

“this”关键字有不同的使用方式,下面是它的示例(方法、正则函数、箭头函数、事件侦听器、显式函数绑定)。

1.方法内部。

this===(对于调用方法的对象)。

在此处输入图像描述在上面的示例中,方法“fullName()”由对象“person”调用,因此方法“full Name())”中该方法的值将等于“person()”对象。

2.函数内部。

i) 函数声明/表达式

在松散模式下,此===窗口(对象)在此处输入图像描述

在严格模式下,此===未定义在此处输入图像描述

注意:使用函数声明或函数表达式方法定义函数时,此属性的工作原理相同。

ii)箭头功能:

箭头函数没有自己的这个属性,它们将这个值作为其周围的函数。如果周围函数不存在,即如果它们是在全局级别定义的,则此===窗口(对象)

在此处输入图像描述

3.事件侦听器这个===对象上附加了处理程序。单击事件绑定到Document对象

在此处输入图像描述

在上面的示例中,由于单击处理程序附加到“document”对象,因此这将等于“documents”对象

4.显式函数绑定(调用、应用、绑定)

call()和apply()方法是预定义的JavaScript方法。

它们都可以用来调用具有另一个对象作为参数的对象方法。

在此处输入图像描述

在上面的示例中,“printFullDetails()”中的这个参数通过传递作为调用方法的第一个参数显式设置为personObj1和personOb2。

您可以了解有关调用、应用和绑定方法的更多信息在这里.

4
  • 这个接受的答案这里是正确的、最新的和完整的。代码示例不应该以屏幕截图的形式出现,而是作为可复制的代码块(也适用于您之前的答案)。“此属性是在每次执行函数时创建的,而不是在此之前创建的”不正确:它缺少类和全局执行上下文。“取决于它的使用方式”非常模糊。“函数的所有者”在JS中不是真正的语言构造。对象不能“调用”方法。可以调用方法(或“关闭”)对象。“正常模式”更少正常的比严格模式… 2021年11月30日18:07
  • 一般来说,全球This应该提到而不是窗口.“箭头函数没有自己的此属性”是模糊的。不是对象的属性(ES语言值),而是环境记录上的内部插槽(ES规范值)。箭头函数不绑定 .“如果它们是在全局级别定义的”然后应用严格模式与松散模式的相同规则。事件监听器实际上并不特别;添加事件侦听器使用调用侦听器函数绑定到当前目标属性。 2021年11月30日18:08
  • 有几个API绑定了一些但您可以创建自己的API来实现这一点。“在上述示例中”? 这个例子是在下面. 2021年11月30日18:08
  • @塞巴斯蒂安·西蒙,我真的很尊重你花时间阅读我的答案。我真的很感激你最新的回答。但我认为它对初学者没有多大用处,因为它太长了,而且,如果我想要一个详细的概念,我会去看官方的MDN文档。我尽可能简短地回答问题。提供的屏幕截图只是为了快速查看,以后我也会尝试放置代码。 2021年12月1日3:46
1

Scope的这种用法如下

<script type=“text/javascript”language=“javascript“>$('#tbleName tbody-tr').each(函数{var txt=“”;txt+=$(this).find(“td”).eq(0).text();\\同上,但synatx不同var txt1=“”;txt1+=$('#tbleName tbody tr').eq(0).text();警报(txt1)});</script>

txt1和txt的值相同在上述示例中$(this)=$('#tbleName tbody tr')相同

1

总结Java脚本:

  • 的价值取决于函数是如何调用的,不是在哪里创建的!
  • 通常由点左边的对象确定。(窗口在全球空间中)
  • 事件侦听器中指调用事件的DOM元素。
  • 当使用调用in函数时新的关键字的值指新创建的对象
  • 您可以操作具有以下功能:呼叫,应用,绑定

例子:

let对象={prop1:function(){console.log(this);}}object.prop1();//物体在点的左边,所以这是物体const myFunction=object.prop1//我们将函数存储在变量myFuncton中myFunction();//我们在全球空间//myFunction是全局对象上的属性//因此它记录窗口对象

事件侦听器示例:

document.querySelector('.foo').addEventListener('click',function(){console.log(this);//这是指调用eventListener的DOM元素})document.querySelector('.foo').addEventListener('单击',()=>{console.log(this);//提示,es6箭头函数没有自己的绑定到此v})//因此,这将记录全局对象
.foo:悬停{颜色:红色;光标:指针;}
<div class=“foo”>点击我</div>

示例构造函数:

职能人员(姓名){this.name=名称;}const me=新人(‘威廉’);//当使用new关键字时,构造函数中的this将引用新创建的对象console.log(me.name);//因此,name属性被放置在使用new关键字创建的对象上。

0

要正确理解“这一点”,必须了解上下文、范围和它们之间的区别。

范围:在javascript中,范围与变量的可见性有关,范围通过使用函数实现。(阅读有关范围的更多信息)

上下文:上下文与对象相关。它是指函数所属的对象。当您使用JavaScript“this”关键字时,它指的是函数所属的对象。例如,在函数内部,当您说:“this.accoutNumber”时,您指的是属性“accoutNumber”,它属于该函数所属的对象。

如果对象“myObj”有一个名为“getMyName”的方法,当在“getMyName”内部使用JavaScript关键字“this”时,它指的是“myObj”。如果函数“getMyName”是在全局范围内执行的,那么“this”是指窗口对象(严格模式除外)。

现在让我们来看一些示例:

<脚本>console.log('这是什么:'+这);console.log(this);</脚本>

在浏览器输出中运行上述代码将:在此处输入图像描述

根据您在窗口对象上下文中的输出,也可以看到窗口原型引用了对象。

现在让我们在函数内部进行尝试:

<脚本>函数myFunc(){console.log('这是什么:'+这);console.log(this);}myFunc();</script>

输出:

在此处输入图像描述输出是相同的,因为我们将“this”变量记录在全局范围内,并将其记录在函数范围内,我们没有更改上下文。在这两种情况下,上下文是相同的,与寡妇对象.

现在让我们创建自己的对象。在javascript中,可以通过多种方式创建对象。

<脚本>var firstName=“诺拉”;var lastName=“Zaman”;var myObj={名字:“主”,lastName:“男爵”,打印名称获取上下文:函数(){console.log(firstName+“”+lastName);console.log(this.firstName+“”+this.lastName);返回此;}}var上下文=myObj.printNameGetContext();console.log(上下文);</script>

输出:在此处输入图像描述

因此,从上面的示例中,我们发现“this”关键字指的是与myObj相关的新上下文,而myObject也具有指向Object的原型链。

让我们再举一个例子:

<body>单击我<脚本>函数printMe(){//Terminal2:此函数在窗口上下文中声明,因此此函数属于窗口对象。console.log(this);}document.querySelector('.btn').addEventListener('click',function(){//Terminal1:按钮上下文,此回调函数属于DOM元素console.log(this);printMe();})</script></body>

输出:明白了吧?(阅读评论)在此处输入图像描述

如果您无法理解上述示例,让我们尝试使用我们自己的回调;

<脚本>var myObj={名字:“主”,lastName:“男爵”,printName:函数(回调1,回调2){//使用此myObj上下文附加回调1this.callback1=回调1;this.callback1(this.firstName+“”+this.lastName)//我们没有用myObj附加callback2,所以默认情况下它是带有窗口上下文的reamin回调2();/*//测试以下代码this.callback2=回调2;this.callback2();*/}}var回调2=函数(){console.log(this);}myObj.printName(函数(数据){console.log(数据);console.log(this);},回调2);</script>

输出:在此处输入图像描述

现在,让我们了解Scope、Self、IIFE和THIS的行为

var color='red';//窗口的属性var对象={color:'blue',//窗口属性printColor:function(){//obj的属性,与obj一起附加var self=此;console.log('打印颜色中--this.color:'+this.color);console.log('In-printColor--self.color:'+self.cloor);(function(){//decleard在printColor内部,但不是对象的属性,它将在窗口上下文中执行。console.log(此)console.log('在IIFE中--this.color:'+this.color);console.log('In IIFE--self.color:'+self.coolor);})();函数nestedFunc(){//declard位于printColor内部,但不是对象的属性,它将在窗口上下文中执行。console.log('nested-fun--this.color:'+this.color);console.log('nested-fun--self.color:'+self.color);}嵌套函数();//在窗口上下文中执行return nestedFunc;}};对象打印颜色()();//在窗口上下文上执行的返回函数</script>

输出非常棒,对吧?在此处输入图像描述

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.