0

Kyle Simpson在他的(伟大的)系列书籍“你不知道 JS”中指出,动态范围和this机制是“近亲”,他还说:

“这个this机制有点像动态范围。” (YDKJS,范围和闭包附录 A

this 什么阻止他说简单明了的动态范围?

另外,在“ this & Object Prototypes ”一书中,也是 YDKJS 系列的一部分,据我所知,Kyle 在讨论如何this工作时并没有提到一次动态范围,所以我有点惊讶他为什么决定不在类比中走得更远......有人知道为什么吗?谢谢

4

2 回答 2

4

我声称this只是“有点像”动态范围而不是实际上的动态范围的原因是基于以下两个观察结果:

  1. 使用对象引用 ( ) 显式访问“范围上下文”的美学this.foo = 1不同于通过词法变量引用 ( foo = 1) 隐式访问它。充其量,这使“范围”的两个系统平行,而不是相同。但与表面上看起来的不同!

  2. 但更重要的是,动态范围传统上被定义为基于调用堆栈的范围链。也就是说,动态作用域是这样一种作用域,其中决定要查阅哪些“作用域上下文”,按顺序,正是当前的函数调用堆栈。但this不是基于调用堆栈本身,而只是堆栈中最近一次调用的方式。

    考虑这种情况:foo()调用bar()bar()调用baz()和 inbaz()引用了x未定义在 中的变量baz()

    • 在词法范围内,查找将是baz(),然后是外部范围baz(),依此类推。bar()并且foo()根本不会被咨询,除非它们恰好是baz().

    • 在动态作用域中,调用堆栈foo()-> bar()->baz() 作用域链,因此无论这 3 个函数在代码库中的词法上存在于何处,都baz()需要查阅 then bar()、 then 。foo()

    • 现在,考虑相同的场景,但this.x在内部进行了引用baz()。调用堆栈foo()-> bar()->baz()在解决this.x. 相反,唯一重要的是如何baz()调用。如果调用站点是baz(),则应用“默认绑定”规则。如果是this.baz(),则适用“隐式绑定”规则。如果是baz.call(..),则适用“显式绑定”规则。如果是new baz(),则适用“新绑定”规则。而已。

    实际上,这不是唯一的事情,这只是做出的第一个决定。一旦确定了对象(又名作用域上下文对象)this指向什么,现在唯一重要的是该对象的原型链,因为这就是将按照原型链链接顺序查询的“作用域”。


概括:

  • 在词法范围内,定义函数的位置是唯一决定使用什么上下文以及以什么顺序来解析变量引用的东西。

  • 在动态范围内,函数被调用的地方是唯一决定使用什么上下文以及以什么顺序来解析变量引用的东西。

  • this基于 - 的上下文中,定义函数的位置调用函数的位置都不相关。因此,this既不是词法作用域,也不是动态作用域。

    这里唯一重要的是调用堆栈(堆栈顶部)中的当前函数是如何被调用的。好吧,这是确定从哪个作用域链开始查找的唯一重要因素。但是一旦确定了该对象,现在该对象的原型链就完全定义了范围解析。

于 2017-06-29T15:26:40.773 回答
3

因为this“变量”是唯一具有动态范围的变量,所以所有其他(“真实”)变量在 JavaScript 中都具有词法范围。相比之下,在“简单而简单的动态范围”中,所有变量都具有动态范围,您无法摆脱它(这真的很难看)。所以当我们想要在我们的动态作用域中拥有多个值时,我们将它们存储在一个对象中,并将它们作为对象的属性来访问this,这与动态作用域有很大的不同,并且还涉及到对象继承。

于 2017-06-25T17:46:15.983 回答