为什么typeof有时会抛出ReferenceError?

Pau*_*per 10 javascript expression typeof language-lawyer referenceerror

在Chrome和Firefox中

typeof foo
Run Code Online (Sandbox Code Playgroud)

评估为'undefined'.

typeof (function() { return foo; })()
Run Code Online (Sandbox Code Playgroud)

抛出错误:

ReferenceError: foo is not defined
Run Code Online (Sandbox Code Playgroud)

这破坏了我对表达式可疑性的概念!到现在为止,我知道没有条件,这foo(function() { return foo; })()是不一样的.

这是标准行为吗?如果是这样,引用ECMAScript标准的相关部分会很有帮助.


编辑:

另一个例子:

typeof (foo)
typeof (foo + 0)
Run Code Online (Sandbox Code Playgroud)

我希望(foo)(foo + 0)抛出一个错误.

但第一个没有错误; 第二个.

Fab*_*tté 17

基本上,typeof操作员检查变量¹是否无法解析并返回"undefined".也就是说,typeof在到达未声明变量¹的GetValue算法之前,返回未声明变量¹的定义值.

引用ECMAScript5.1§11.4.3运营商类型(重点增加):

11.4.3运营商的类型

生产UnaryExpression:typeof UnaryExpression的计算方法如下:

  1. val是评估UnaryExpression的结果.
  2. 如果Type(val)是Reference,那么

    2.1.如果是IsUnresolvableReference(val)true,则返回"undefined".

    2.2设val为GetValue(val).

  3. 根据表20返回由Type(val)确定的字符串.

另一方面,return语句 - 就像大多数操作符和从标识符中读取值的语句一样 - 将始终调用GetValue哪些抛出不可解析的标识符(未声明的变量).引用ECMAScript5.1§8.7.1GetValue(V)(重点补充):

8.7.1 GetValue(V)

  1. 如果类型(V)不是参考,则返回V.
  2. 设base是调用GetBase(V)的结果.
  3. 如果是IsUnresolvableReference(V),则抛出ReferenceError异常.

现在,分析代码:

typeof (function() { return foo; })()
Run Code Online (Sandbox Code Playgroud)

此代码将实例化一个函数对象,执行它,然后typeof才会对函数的返回值进行操作(函数调用优先typeof运算符).

因此,在评估操作return之前,代码会在评估IIFE 语句时抛出typeof.

一个类似但更简单的例子:

typeof (foo+1)
Run Code Online (Sandbox Code Playgroud)

之前评估添加typeof.当这将抛出一个错误加法运算符要求GetValuefoo,之前typeof进场.

现在:

typeof (foo)
Run Code Online (Sandbox Code Playgroud)

不会抛出错误,因为分组操作符(括号)本身并不"评估"任何内容,它只会强制优先级.更具体地说,分组操作员不会打电话GetValue.在上面的示例中,它返回一个(不可解析的)引用.

注释的ES5.1规范甚至添加了一个关于此的注释:

注意此算法不适GetValue用于评估Expression的结果.这样做的主要动机是使运算符如deletetypeof可以应用于带括号的表达式.


NB我写了这个答案,重点是提供一个简单易懂的解释,将技术术语保持在最低限度,同时仍然足够清晰并提供所要求的ECMAScript标准参考,我希望这对于那些奋斗的开发人员来说是一个有用的资源.了解typeof运营商.

¹术语"变量"用于便于理解.一个更正确的术语是标识符,它不仅可以通过变量声明引入词法环境,还可以引入函数声明,形式参数,调用函数(arguments),with/ catch块,将属性赋值给全局对象,let以及const语句( ES6),可能还有其他一些方法.

  • @FabrícioMatté 是的......这可能是迄今为止最好的答案恕我直言 (2认同)

Rob*_*obG 8

这是标准行为吗?

是.typeof不会抛出错误,因为它只返回指定的值.但是,正如其他答案所述,代码在评估操作数时失败.

如果是这样,引用ECMAScript标准的相关部分会很有帮助.

在计算函数表达式时,尝试解析foo的值(以便可以返回它)将使用参数foo调用内部GetValue方法.但是,由于未声明或以其他方式创建foo,因此会引发引用错误.

编辑

如果是:

typeof (foo)
Run Code Online (Sandbox Code Playgroud)

"("和")"是标点符号,表示分组,例如在调用函数时可能为空的参数列表foo(a, b),或者表示要评估的表达式,例如if (x < 0)等等.

typeof (foo)他们简单地表示在应用typeof运算符之前评估foo的情况下.因此,作为有效标识符的foo被传递给typeof,上面的每个链接尝试解析它,不能确定它是不可解析的引用,并返回字符串"undefined".

如果是:

typeof (foo + 0)
Run Code Online (Sandbox Code Playgroud)

括号导致foo + 0首先计算表达式.获取foo的值时,会抛出引用错误,因此typeof无法运行.请注意,没有括号:

typeof foo + 0 // undefined0
Run Code Online (Sandbox Code Playgroud)

因为运算符优先级:typeof foo返回字符串"undefined",所以+成为加法运算符,因为其中一个参数是一个字符串,它做连接(加法的字符串版本,而不是数学版本),所以0转换为字符串"0"并连接到"undefined",重新进入字符串"undefined0".

因此,只要尝试使用不可解析的引用来评估表达式(例如,未声明或初始化的变量),就会抛出引用错误,例如

typeof !foo 
Run Code Online (Sandbox Code Playgroud)

抛出引用错误也是因为为了找出要传递给typeof的内容,必须对表达式求值.要应用!运算符,必须获取foo的值,并在尝试时抛出引用错误.