回答

收藏

“this”关键字如何工作?

技术问答 技术问答 234 人阅读 | 0 人回复 | 2023-09-12

我注意到this网站上似乎没有明确解释关键词是什么,它是如何解释的 JavaScript 正确使用(和错误)。
# e! ~1 J* p1 o0 |  e我目睹了它的一些非常奇怪的行为,不明白它为什么会发生。1 H8 X3 W; [9 t- a' {6 E
如何this什么时候工作应该用?
9 r+ q+ o+ Z2 h2 l1 i5 y                                                               
: ~4 n2 N0 o& f5 P) e1 V, u' r    解决方案:                                                               
, h* q: s! @. ^- r+ K( T                                                                this是 JavaScript 中的关键字是执行上下文的属性。其主要用途是函数和构造函数。2 `$ H4 q9 B3 h9 W* M$ ^" ~
我建议先读Mike West的文章JavaScript 作用域(存档)。这是对的thisJavaScript 优秀友好地介绍了作用域链和作用域链的概念。this很简单(如果你坚持最好的练习)。
& w' P! v! w0 |! E8 C1 k9 B1 jthis规范中的技术说明的ECMAScript标准定义this抽象操作(缩写)AO)ResolveThisBinding:
$ c  ?- t) u( U( X. w/ B3 J[AO] ResolveThisBinding […]this上下文使用正在运行的 LexicalEnvironment 确定关键词的绑定。[步骤]:
/ Z% [" P! l2 s% b[ol]令envRec为GetThisEnvironment ()。. _" w6 `: e1 ^% x% f% K2 |' U
返回 ?envRec    .GetThisBinding()。[/ol]全球环境记录、模块环境记录和函数环境记录都有自己的 GetThisBinding 方法。
8 E3 u3 U$ ^/ m' R6 [( U9 C% f: l所述GetThisEnvironment AO找到目前正在运行的上下文执行情况LexicalEnvironment和查找最接近方兴未艾环境记录(通过迭代地访问他们的[[OuterEnv]]特性),它有这种结合(即HasThisBinding返回)。这一过程以三种环境记录类型之一结束。
2 z& q. K, H1 T+ N1 \  A- ]/ Y的值this这通常取决于代码是否严格。
  W3 t1 }1 p& f$ p) Z$ i: WGetThisBinding 反映了返回值this目前执行上下文的值,所以无论何时建立新的执行上下文,都会this分析是一个不同的值。这也可能发生在修改当前执行的上下文时。以下部分列出了可能发生的五种情况。
$ C2 s# M9 b& U! E1 D2 D您可以在代码示例中放置代码示例AST 资源管理器遵循规范的详细信息。5 j/ |8 c8 I  Y" L8 J& u- x* e- L/ P
1. 脚本中的全局执行上下文这是顶层评估的脚本代码,比如直接在 a 中[/code]在上下文中对脚本的初始全局进行评估时this会导致GetThisBinding采取以下步骤:
7 X9 \& ^: q) K. Y/ r" v全局环境记录envRec    […] GetThisBinding 具体方法:
; O1 x' c; Z- V4 }  H" k2 Y[ol]返回envRec    .[[GlobalThisValue]]。[/ol]全局环境记录[[GlobalThisValue]] 属性总是设置为主机定义的全局对象,可以通过globalThis(window在 Web 上,global在 Node.js 上;MDN访问文档)访问对象。InitializeHostDefinedRealm了解 的步骤[[GlobalThisValue]] 属性是如何形成的。0 v" [5 P5 ?/ k1 F. U4 K
2.模块中的全局执行如下模块已在 ECMAScript 引入2015 。% a0 R1 W5 N- K$ M6 o
这适用于模块,如直接在 a 内部时,而不是简单.
; a! |1 S3 H  ~  l7 {在模块的初始全局实施中,求值this会导致GetThisBinding采取以下步骤:* `2 _9 u. {9 G7 S
记录模块环境GetThisBinding 具体方法 […] [这样做]:" N2 t2 @* j1 Q( v$ ~% j- D
[ol]返回undefined。[/ol]在模块中,值this总是undefined全局上下文。模块隐式严格。4 F6 |, S" j! U9 D0 _
3. 输入评估码有两种eval调用:直接调用和间接调用。这种区别自 ECMAScript 第 5 版存在。
. O0 N1 l8 z( [直接eval调用通常看起来像eval(……);或(eval)(…… );(或((eval))(……);等等。直接的。27 x. K4 ]5 f  k. t  J9 u
间接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’) vs eval(‘this’)”的回答?和Dmitry Soshnikov 的 ECMA-262-5 详细信息-第 2 章:严格模式(已存档),知道何时间接使用eval()调用。
1 e9 [, U- q0 Y' rPerformEval执行eval代码。它创建了一个新的声明环境记录作为 LexicalEnvironment,这是GetThisEnvironment 从中获取this值的地方。$ r. ?9 ~  y+ a, k3 J1 _( p
然后,如果this出现在eval环境记录在代码中GetThisBinding方法发现GetThisEnvironment它被称为返回值。' A4 l3 ^2 G% r( u+ ?$ _
声明环境记录的创建取决于eval调用是直接还是间接:* }, y8 w4 C6 }+ S+ t. }+ J
在直接 eval 中,它将基于当前操作的上下文LexicalEnvironment。
# ~$ p! c  T" [' C1 ]) [2 ^在间接评估中,它将记录在基于间接评估的领域[[GlobalEnv]] 属性(全局环境记录)。
意思是:' I. ~" p) X, ^; u
在直接 eval 中,this值不会改变;它自称是eval.
' |- v+ k. ]4 _; x* U% q' Y0 e在间接 eval 中,this ( )globalThis)。
怎么样new Function??—?new Function类似于eval,但它不会立即调用代码;它创建了一个函数。本除非函数被调用,否则任何地方都不适用于装订,如下一节解释其正常工作。+ n( n( b* {4 f/ K7 ^/ ]
4. 输入功能码调用输入函数代码将出现在函数中。/ ?- ~# H2 f  r# D& ~- L
调用函数有四种语法。
, C* Q* Q2 p1 r' p  h+ X- SEvaluateCall7 k' U' u3 F: l( A2 h
AO这三种执行:' q3 D7 w6 |% f2 V) j7 Q* ~0 P, L
3( t5 c3 C4 H, w6 B; z* N
调用普通函数
! w7 C" `  ]1 {+ H5 @6 H$ O可选链接调用  i$ J8 T' i" q/ J% u
标记模板
# _* @" d- D  L2 X并为此执行
2 g5 L- }7 [9 g3 N" d: X$ V( n
EvaluateNew
2 R5 Z9 Y2 u$ Y' P! j$ J& K2 i1 ~/ L' R+ g! q1 M8 T  e9 [3 n
3
8 k4 G& F$ H  v! F% |0 H, l( j* t调用结构函数实际函数调用发生在Call AO 处,调用时使用根据上下文确定的thisValue;该参数在与调用相关的长串调用中传递。Call调用函数的[[Call]]内槽。这将被调用PrepareForOrdinaryCall,创建了新的函数环境记录:( N, e7 k& S) H) d1 V. |
甲功能环境记录如果函数不是,则声明环境记录用于表示功能的顶层范围和ArrowFunction,提供了一种this如果函数不是ArrowFunction函数并引用super,还包括函数环境记录super调用函数内部执行方法的状态。/ z% h9 D9 P6 B. |
另外,在函数 Environment Record 中有 [[ThisValue]] 字段:' F  p/ G7 Z+ U& i% v* [- Q$ h1 q, p
这是this用于此函数调用的值。1 x  o8 Y) @0 k& x  W7 u- U) |  U
该NewFunctionEnvironment通话还设置了功能环境[[ThisBindingStatus]]属性。; M, n: U& k  a* a
[[Call]]还调用OrdinaryCallBindThis,其中适当的thisArgument基于以下因素:
# O1 m! Y- Y& T原始参考,, o5 @' p/ V9 L* ?1 S# S
函数的类型,以及
+ s1 a; k3 B, l; a代码是否严格。
一旦确定,新创建的函数 Environment Record的BindThisValue实际上,方法的最终调用会 [[ThisValue]] 字段设置为thisArgument。
( d" v$ m6 d$ Y5 L0 ~最后,这个字段是函数 Environment Record 的 GetThisBinding AOthis从以下位置获取值:
+ f* g% b4 L* A8 |# u  \函数 Environment Record envRec    […] GetThisBinding 具体方法:3 i2 l# m# b) }( d6 U& a
[…]+ ~* L5 R8 o8 [7 c' F; d% L3 g/ D( ~
\3. 返回envRec    .[[ThisValue]]。
5 |; @+ x& g. n" w$ J1 _2 t5 S7 l# {同样,该确定值的方法取决于许多因素;这只是一个总体概述。有了这个技术背景,让我们检查一下所有具体的例子。
3 p! O/ n+ k" h( M" m/ D箭头函数在评估箭头函数时,函数对象 [[ThisMode]] 内部槽在OrdinaryFunctionCreate 中设置为“词法”。
" z& c; ?8 M( ?7 N( K在OrdinaryCallBindThis,它接受函数F:
7 h6 V1 S1 @: F[ol]令thisMode为F    .[[ThisMode]]。
, a# B. g; i! [; e- K7 u* V5 H/ u如果thisMode是词法的,则返回 NormalCompletion( undefined)。[…][/ol]这只意味着跳过绑定this的算法的其余部分。箭头函数不绑定自己的this值。8 m; |8 i6 P& S: ~6 t& L% `  o
那么,this箭头函数内部是什么?ResolveThisBinding和GetThisEnvironment,HasThisBinding 方法显式返回false/ ^/ ?" j7 F9 Q. Z& y; |  \, K/ O
HasThisBinding 函数的具体方法 Environment Record envRec    […] [这样做]:
/ H9 }' |  r3 e[ol]如果envRec    .[[ThisBindingStatus]] 是lexical,则返回false;否则,返回true。[/ol]因此,外部环境被迭代地发现。这个过程将有this三个环境之一的绑定结束了。. A9 A# Z  S! |, W8 d
这只是意味着,在箭头函数体中,this箭头函数的词法范围,或者换句话说(箭头函数和函数声明/表达式:它们等效/可交换吗?. w; F  `" l$ B
箭头函数没有自己的this[…] 绑定。相反,[此标识符]在词法范围内像任何其他变量一样进行分析。这意味着在箭头函数中,this[引用] 到定义this在箭头函数的环境中[值] (即箭头函数的外部)。9 ?" A0 E; e* R4 y9 [
功能属性在正常功能(function,方法),this来确定如何调用所述函数?+ p1 q% l) f: V
这些语法变体派上用场。
; i" X) ]! P$ [* M+ }- _考虑包含函数的对象:# w, {5 f6 E! d1 a7 \. A
    2 t" A" F" j- ^: T
  • const refObj =    func: function(){       console.log(this);    } }code]或者:! K5 I9 \; r) [* Q- u
  • [code]const refObj =    func(){      console.log(this);    } }code]在以下任何函数调用中,this里面的值func都是refObj. 12 P9 }* c2 A5 s2 t% {

  • 8 P2 t% P1 S& \; F2 _- H
  • refObj.func()/ E2 I* h8 O  |  M( ?
  • refObj["func"]()
    ) J. I# P" x5 P
  • refObj?.func()  ~6 E- ~6 W& T# O/ r
  • refObj.func?.()
    2 V9 ^1 b! {% U8 Q) P
  • `refObj.func```如果被调用的函数在语法上是一个基对象的属性,那么这个基将是调用的“引用”,在通常情况下,它是 的值this。解释了上述链接的评估步骤;例如,在refObj.func()(或refObj["func"]())中,CallMemberExpression是整个表达式refObj.func(),它由MemberExpression    refObj.func和Arguments 组成    ()。  V4 o" F7 `* S& @, o# R
  • 而且,refObj.func并refObj扮演三个角色,每个角色:
    & m2 s! o* z. L9 R9 p2 i

  • , [. @6 l  m4 [7 A0 G
  • 都是表达,
    + v) B! v( ^! \1 q- N, Q
  • 而且都是参考
    3 R; A8 s( U% ?8 r! G0 o$ g
  • 都是价值观。refObj.func作为值可调函数对象;相应的参考用于确定this绑定。
    + [; B9 j; }- ^  S7 W6 K; w
  • 可选链接的工作方式与标记模板示例非常相似:基本上,引用是在?.(), 之前```或 以前的所有内容()`。
    ) x; k" k% m( ]. L/ Z3 Q
  • EvaluateCall使用此引用IsPropertyReference确定它是否是语法上的对象属性。它试图获得引用 [[Base]] 属性(例如refObj,当应用于refObj.func; 或foo.bar应用于 时foo.bar.baz)。假如把它写成属性,那么GetThisValue将获取此 [[Base]] 属性并将其用作此值。7 L: d2 |9 N3 q( ]3 `( |+ P9 E$ N
  • 注意:关于.getter/setter 的工作方式和方法相同this。例如,简单的属性不会影响上下文的执行this在全局范围内:
    ! n9 Y8 C# H% O7 M2 ]6 M
  • [code]const o = {    a: 1,   b: this.a,// Is `globalThis.a`.    [this.a]: 2 // Refers to `globalThis.a`. }code]没有基本引用,模式严格, with没有基引的调用通常是不作为属性调用的函数。[code]func(); // As opposed to `refObj.func();`.2 c0 M, d9 y& [: C% [& X) B7 v; G
这种情况也发生在传输或分配方法或使用逗号操作符时。这是参考记录和值之间的差异。- D0 M5 |# D9 T! K
    const g = (f) => f(); // No base ref.const h = refObj.func;g(refObj.func);h(); // No base ref.(0,refObj.func)(); // Another common pattern to remove the base ref.  T3 w4 Z2 v- d1 y# ?/ h
EvaluateCall电话呼叫与thisValue的不确定这里。这在OrdinaryCallBindThis(F:函数对象;thisArgument:传递给Call的thisValue)不同:8 {! a8 f7 h$ z
[ol]令thisMode为F    .[[ThisMode]]。[/ol][…]& b8 F8 @7 e) O, V1 E
[ol]如果thisMode是严格的,则让thisValue为thisArgument。
, ?9 v" `# i2 d5 ]* b别的,, R# U" d  v$ I) ~( T
如果% t! O% s$ I: E( f# p: k
thisArgument: v, g. ]+ q' _3 c5 i
. T4 Y: V8 k' }3 ~$ }1 [
undefined
/ r- h" d" N2 ]% V, K9 u/ G* N# f/ s5 G* G! I' C
null
  j. d6 h( G. P- d( \8 ?4 B0 t) x,则" S" p/ H9 u, `9 Y& e; {2 W7 T
[ol]令globalEnv为calleeRealm    .[[GlobalEnv]]。! G2 _! T7 t' P
[…]7 l8 a3 c: K# ?8 G! b) ~- ]* d; }
令thisValue为globalEnv    .[[GlobalThisValue]]。[/ol]
" E- y0 l/ ^  F: J' C& C别的,
. e7 q: x* N5 i7 l7 v. {[ol]让thisValue成为!ToObject (thisArgument)。8 Q" }9 N, s6 S! K# C7 ~
注意:ToObject生成包装对象 […]。[/ol][/ol][…]8 j0 D& Z/ N  L4 ?
注:第 5 步在严格模式下设置 的实际值this为提供的thisArgument    -?undefined在这种情况下。在草率模式中,模式thisArgument导致this成为全局this值。
; u9 W1 A- p% ~& a7 F; N如果IsPropertyReference返回false,则EvaluateCall采取以下步骤:
& r: A: k7 c4 z5 p+ i& O: e( m# `; q[ol]让refEnv为ref    .[[Base]]。2 d7 h7 ?: e; H# P1 R) N. v
断言:refEnv是环境记录。
0 d- ?( `5 n2 j  |7 _8 S- |" d& u让thisValue为refEnv    .WithBaseObject()。[/ol]这是未定义的thisValue可能来自:refEnv。WithBaseObject()总是不确定的,除了在with声明。在这种情况下,thisValue将是绑定对象。9 o* a: g" \- e8 ]" ?2 r, O# l
还有Symbol.unscopables(MDN控制 文档)with绑定行为。5 Z) T& o1 Q6 L6 O
综上所述,到目前为止:
; v* D& k7 X+ c/ |8 T1 ~
    function f1(){  console.log(this);}function f2(){  console.log(this);}function f3(){  console.log(this);}const o =    f1,   f2,   [symbol.unscopables]:      f2: true   f(); / Logs `globalThis`.with(o){  f(); / Logs `o`.  f(); / `f2` is unscopable,so this logs `globalThis`.  f(); / `f3` is not on `o`,so this logs `globalThis`.}4 U2 V6 O3 U8 j( ]7 i6 r( n7 B
和:4 I! V2 Q2 S( ^: y7 h& b# z; k
    "use strict";function f(){  console.log(this);}f(); // Logs `undefined`.// `with` statements are not allowed in strict-mode code.* j& ]  e3 l  o2 h( j9 u
评估时应注意this,它并不重要*,其中*函数定义正常
1 I& s! e9 o& U* i) W9 `. z: O.call,.apply,.bind,thisArg和原语OrdinaryCallBindThis第 5 步和第 6步.2 步(规范中的 6.b)另一的另一个结果是,仅在草率模式下才将原始this值强制转换为对象。9 L& x9 s$ C1 u
为了检查这一点,让我们this价值引入的另一个来源覆盖this三种绑定方法:4% p7 S( d) _$ i' u4 S* t/ K
Function.prototype.apply(thisArg,argArray)
3 I" M' K8 i* J4 l  X; [Function.prototype.{ call,bind}(thisArg,...args)
.bind创建绑定函数this绑定设置为thisArg而且不能再改了。.call并.apply立即调用函数并将其调用this绑定设置为thisArg。
$ Z7 Q1 W7 W- Z: i6 a& [. V! @.call`并使用指定的*thisArg*`.apply`直接映射到[Call](https://tc39.es/ecma262/#sec-call)。使用[BoundFunctionCreate](https://tc39.es/ecma262/#sec-boundfunctioncreate)创建绑定函数。*自己的*[[[Call\]] 方法](https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist),  用于搜索函数对象[[BoundThis]] 内部插槽。`.bind设置自定义this值的示例:: O" J7 m$ _$ s# R4 U% }# A9 \5 t
    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);
    - k" O; e8 [- N4 g
在严格模式和非严格模式下,对象是一样的。
7 O. G; B$ J3 ?% B8 A6 D试着提供一个原始值:% t% w5 q  K' p  y
    function f(){  console.log(this);}const myString = "s", g = f.bind(myString);g();;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Logs `String { "s" }`.f.call(myString); // Logs `String { "s" }`.
    - c8 _) T) K8 F  v# P
在非严格模式下,原语被迫转换为其对象包装形式。它正在调用你Object("s")or获得相同类型的对象new String("s")。在严格的模式下,你可以使用原语:4 u6 M% h, T* M% s1 G. r
    "use strict";function f(){  console.log(this);}const myString = "s", g = f.bind(myString);g();;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Logs `"s"`.f.call(myString); // Logs `"s"`.
    3 k4 C5 e& j5 C
如 jQuery 设置 this 在这里选择DOM 元素:8 v( i2 O8 o( N) {5 ?: b, G& J; k0 z* L
    $("button").click(function(){  console.log(this); // Logs the clicked button.});
    / t1 f7 b& t7 }! X
构造函数,类和new当使用new当运算符调用函数作为构造函数时,EvaluateNew调用Construct,后者调用[[Construct]] 方法。如果函数是基本结构(即,不是class extends… {… },它的设置thisArgument从构造函数的原型创建新对象。this构造函数中设置的属性最终会出现在生成的实例对象上。this隐式返回,除非你显式返回自己的非原始值。
& L6 A; E/ @: w1 [3 Z2 }  W, ]+ R% ~Aclass在 ECMAScript 引入2015 。5 O2 H# @9 t6 c) N/ I& k. m9 \/ v
    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 }`.
    - I5 ?) r5 X7 Q6 K+ M1 M
严格模式中隐含类定义:
0 }( {7 @- P/ d7 z, X: t
    class A{  m(){     return this;  }  m(){     const m1 = this.m1.         console.log(m();new A().m(); / Logs `undefined`.3 [$ F" f. Z" M/ G
super行为的例外new是class extends… {… },如上所述。调用时不会立即设置衍生类this值;他们只在super调用后这样做(在没有自己的情况下隐式发生constructor)。使用this呼叫之前super不允许。
) _, S/ q+ ~; Q4 O6 n! ~调用使用super调用词法范围(函数环境记录)this值调用超级结构函数。GetThisValue有一个特别的super调用规则。使用它BindThisValue设置this记录环境。! `5 r" [3 k9 I0 o' l" N
    class DerivedNew extends New{  constructor(a,a(2){    / )Using `this` before `super` results in a ReferenceError.    super(a);    this.p2 = a2;  }}const n2 = new DerivedNew(1,2);console.log(n二、 / Logs `DerivedNew { p: 1,p2: 2 }`.
    ; q: n( ~+ F8 O* E
5. 评价字段ECMAScript实例字段和静态字段引入 2022 。$ M; `# J9 u6 S
当class评估 a 时,执行ClassDefinitionEvaluation,修改正在运行的上下文。对于每个人ClassElement:
" N0 T  O% J4 L8 j- w0 k8 L0 k若字段静态,则this指类本身,$ ^, `$ P* H7 t5 |7 s% C2 D
若字段不静态,则this引用实例。
私有字段(例如#x)添加到 的方法PrivateEnvironment。
% L6 g+ m# {/ P; h目前静态块是TC39 第 3 阶段提案。静态块的工作方式与静态字段和方法相同:this它们内部是指类本身。3 y8 _/ h, k6 @, x/ Q- Z+ ~4 u4 U" l
方法和 请注意getter/setter 中,this就像在普通函数属性中一样工作。
! b+ J- h$ a. @
    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`.
    , \) G0 _: R- L
1 o.f)()相当于o.f(); (f)()相当于f()。这篇文章 2ality 文章存档)解释。特别是查看ParenthesizedExpression如何计算。
. Q" I! P$ A$ M8 i2:它必须是MemberExpression,不能是属性,必须有 [[ReferencedName]] 正好是“eval”,必须是 %eval% 内在对象。8 H, _: H- j& k9 s- I4 P+ p% E
三、每当规范说“让ref成为对X求值的结果。”,那么X是一些表达式,你需要找到求值步骤。例如,评估MemberExpression或CallExpression结果是这些算法之一。其中一些导致参考记录。
2 V" }$ @6 {- w1 j& E$ b4:还允许提供其他本地和主机方法这个值,特别是Array.prototype.map,Array.prototype.forEach等接受一个thisArg作为他们的第二个参数。任何人都可以用自己的方法来改变this一样(func,thisArg) => func.bind(thisArg),(func,thisArg) => func.call(thisArg)像往常一样,MDN提供巨大的文档。
! A" p0 A" p4 X+ L' K+ _9 {只是为了好玩,用一些例子来测试你的理解回答每个代码片段的问题:“this标记行的值是多少?为什么?    .2 `2 `& _1 G* C) M- u. Y3 E
单击灰色框以显示答案。
# J# o5 B4 ]8 ~: }$ i( O; j" x[ol]js   if(true){     console.log(this); // What is `this` here?   }[/ol]``
# y- h( Y. F; {1 y4 ?5 k2 e[ol][/ol]```js2 y0 u5 q, f. b) h4 P$ g8 D
            const obj = {};
! m9 N( U( i2 a8 ^' dfunction myFun(){
4 t2 J$ l7 s+ L& O" t                    return { // What is this here?
1 Q: H! f1 @0 M                            “is obj”: this === obj,- p& d2 E( R2 S
                            “is globalThis”: this === globalThis6 V" u4 J8 T2 s" Q0 W+ n6 \
                    ;& P$ e0 h  B- A! p& O
            }7 p6 ]% E/ W, p8 Z! U) |2 l' t
obj.method = myFun;
! \  ~2 c2 m) y# {! O7 f* |console.log(obj.method());! t3 A. Y; b& T$ K
```
0 U7 s8 X+ n/ B7 g7 [$ |, \& uRun code snippetExpand snippet
8 t$ }& d( }& Q' V``````
7 W; D" b# j- S. A! |& b1 S[ol][/ol]``js   const obj = {       myMethod: function()()()(){                                 return { // What isthis` here?# R3 k: H7 B& y2 I, }* w' c
                                            “is obj”: this === obj,
1 @: u1 {* @( a  L; u9 z+ `  f                                            “is globalThis”: this === globalThis
) \; y% q; o9 I# \: M                                    
1 v' B3 U/ {/ F! W# m: ?3 H                           
' X5 h0 ]. f3 _+ d; o% h- h                    
2 X0 u% K+ j3 P+ f% d2 j                    myFun = obj.myMethod;8 Q/ X8 g* X! C, l8 |) N+ N
console.log(myFun());/ Q3 B  V5 H' z5 y6 K2 ~' \
```
8 Q' x0 v) S5 B  h# @5 y4 L2 gRun code snippetExpand snippet
+ o9 O: l. y3 |' }``````````/ S" y- G- `% o) R  P+ [4 \
[ol][/ol]``js   const obj =          myFun: () => ({ / What isthis` here?; F- A5 K: }$ T8 e, u! l* T
                                    “is obj”: this === obj,
4 g- Q& C9 t2 R! d! d                                    “is globalThis”: this === globalThis, ]; m/ t2 c1 I2 @+ Q0 ~" p8 C
                            & V3 h5 P( d1 t3 A# A0 ^
                    ;
9 T' z/ q! v; O, zconsole.log(obj.myFun());1 T+ ^6 x# J4 i5 S$ k0 W
```
: A& R5 N' Y& ^5 h; H) yRun code snippetExpand snippet8 [) `3 O' j3 J* b
``````
: U9 I; J  e# D4 ^5 r2 \& H[ol]``js   function myFun(){      console.log(this); // What isthis` here?" P& c+ @, N( u
            }[/ol]const obj = {
( |2 z+ t5 g$ I. i: P                            myMethod: function(){9 E. M: L! k6 P
                                    eval(“myFun()”);  ]! B7 P1 O1 B7 \! A6 t; ~  q
                           
8 p* M# D* O9 A% F5 V5 B: J                    ;! a8 _+ e& c5 ~/ C$ }
obj.myMethod();
; K" }5 x" z4 S# D+ b; F! a1 h            ```
0 S$ L$ b) {% T" [+ \$ j````````6 A8 @. {/ c4 ]
[ol][/ol]``js   function myFun()()(){     / / / / What isthis` here?
% p8 M5 \: x, T6 ]                    return {
. J% T$ U# _* J; F1 k8 V6 p                            “is obj”: this === obj,
$ C& N/ |0 K: z, j0 V                            “is globalThis”: this === globalThis
, t8 L5 L' @& l8 Y4 W" }                    };+ ]! W% Y* K2 f* s5 P  D
            }
4 m0 @3 a! D2 _: a: o9 ?const obj = {};( C7 o+ y% Q% i
console.log(myFun.call(obj));
- G: ^3 a$ b+ f: u; L9 h7 D5 p```
0 K, |7 B9 d, `* {# d" @Run code snippetExpand snippet  t( q% {9 p) O8 s" `( ?
````````
, _2 c4 n: {6 ?4 S/ l1 T; L[ol][/ol]``js   class MyCls{     arrow = () => ({ / What isthis` here?
, H  @+ ]6 b2 z9 o. ]                            “is MyCls”: this === MyCls,: \8 L3 H) V! _3 ^
                            “is globalThis”: this === globalThis,
& G; l5 u7 p4 Z# b- Z+ E2 I0 t! |# y                            “is instance”: this instanceof MyCls
" O* l- N- X9 x6 C* j8 N                    };: i; [1 u, C0 B! l
            }
* N- A2 C- _! N3 S8 ]console.log(new MyCls().arrow());5 c0 f$ q4 s' Z6 j, @
```
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则