当你调用一个函数时,JavaScript引擎中会发生一件特殊的事情;这是规范中可以找到的一种行为,它允许这
在方法调用中自动绑定的值。
ECMAScript定义语法bar.baz.foo()
作为一个调用成员表达式.
bar.baz.foo();//呼叫成员表达////│О—成员表达式//^^^│∏—标识符名称//^^——参数
就是这样的评价的包括拆分调用成员表达式成为其组成部分成员表达式和论据.然后成员表达式是评价的将其分解为其组成部分成员表达式和标识符名称.这个成员表达式s都是递归处理的,并且评价的作为的单个值语言类型(即一种常见的JavaScript类型,通常是Object类型)。
最后,所谓的规格类型生成:即参考记录.这些参考记录是具有四个属性的键值对,但相关的是[[基础]]
和[[参考名称]]
.这个[[基础]]
属性包含的值棒材
(计算的嵌套成员表达式)、和[[参考名称]]
是字符串“foo”
(的字符串值标识符名称).这个是函数调用继续进行的内容。
这个规范类型与语言类型.规范类型的值在语言本身中是不可见的,它们可能并不实际存在。规范类型仅“存在”于帮助解释规范中的概念,但实现可以自由选择任何合适的表示,只要其行为等效于规范性规范文本。
函数调用评估的最后一步是“返回EvaluateCall(评估呼叫)(函数,裁判,论据,尾声呼叫)“,其中函数是(的)函数对象语言类型对象)bar.baz.foo酒吧
和裁判是引用记录{[[Base]]:棒材
,[[ReferencedName]]:“foo”
}.EvaluateCall的最后一步是:“返回呼叫(函数,this值,参数列表)”.当函数调用最终在此处启动时,它接收要调用的函数对象(函数),的值这
(this值)它直接来自Reference Record的[[Base]]属性(少数特殊情况除外)参数列表来自论据.这看起来非常接近函数调用(thisValue,…argList)
在JavaScript中,其中函数===bar.baz.foo
和此值===bar.baz
.
我希望,这种可视化有一些用处:
bar.baz.foo();//调用成员表达式//成员表达────────────────────┐//成员表达式(作为对象)//^^^│∏—标识符名称─┐ │ │ ┌─────────────────────┐//^^——参数─┐ │ │ EvaluateCall(func,ref,arguments,tailCall)│// │ │ │ │ │ └───┐ │// │ │ │ 调用(func,│thisValue,argList)│// │ │ │ ┌───────────────┘ │ │// │ │ │ 引用记录{[[Base]],[[ReferencedName]]}│// │ │ │ │ │ │// │ │ │ (作为对象)(作为字符串)│// │ │ └──────────────────────────┘ │ │// │ └──────────────────────────────────────┘ │// └─────────────────────────────────────────────────────────────────┘
但是表达方式bar.foo()
,(bar.foo)()
,以及类似的bar.baz.foo()
,(((bar.foo))()
,等等特殊的因为它们唯一地保存函数调用的引用记录。几乎所有其他表达式,如(bar.foo=bar.foo)()
,(0,bar.foo)()
,(空??bar.foo)()
,等等不.这主要是因为他们只是被不同的评价;换句话说:JavaScript就是这样工作的,因为规范是这样规定的。
虽然理论上可以重写规范并重新设计语言(0,bar.foo)()
或const foo=bar.foo;
将保留Reference Record或类似内容(请参阅Python及其绑定方法),这将附带一个巨大的兼容性影响,所以我们无法真正改变行为。我认为之所以选择这种行为,是因为JavaScript最初被设计为简单易懂语言和上下文之间的区别const foo=(0,bar.foo);
生成一个语言类型的值,但是(0,bar.foo)()
保持规范类型的值对于JavaScript作为Web语言的早期目的来说太复杂了。
即使在变量赋值的情况下,也会丢失引用记录,因为您可以观察赋值,所以它有语言类型:
const foo1=bar.foo;//通过记录“foo1”可以观察到值“bar.foo”。console.log(foo1);//函数对象。//你永远不会在这里看到ReferenceRecord{[[Base]]:bar,[[ReferencedName]]:“foo”},因为这在语言中不存在。
请注意,将某个东西作为参数传递或从函数返回某个东西也算作分配.
const回调函数=(回调)=>{//“callback”是一个函数对象,而不是引用记录。回调();返回回调;};backcaller(bar.foo)//返回值必须是语言类型,因此这是一个函数对象,而不是引用记录。()
另请参阅:
完成一般解释后,现在让我们解决您的问题的一些具体问题:
表达式bar.foo=bar.foo
收益a值; 该值是位于的函数对象巴.福
.具体来说,它必须是语言类型,因此它不能是引用记录。这个规范说“让右心室是吗?获取值(雷夫)”,后跟“返回”rval公司”.简而言之,GetValue要么返回语言类型的值,要么抛出引用错误
.
(bar.foo)()
与相同bar.foo()
.发件人巨大的这
回答:
这在中进行了解释这篇2岁的文章(存档的).特别是看如何带圆括号表达式已评估.
运行时语义只有一个步骤和一个注释:
带圆括号表达式:(
表达式)
- 返回?表达评估。这可能是Reference类型。
注释
此算法不将GetValue应用于表达式求值。这样做的主要动机是删除
和类型
可以应用于带括号的表达式。
果然,删除
和类型
需要能够接受参考记录,因此它们也以同样的方式“特别”。