为什么typeof有时会抛出ReferenceError?

时间:2014-06-10 20:58:20

标签: javascript expression language-lawyer typeof referenceerror

在Chrome和Firefox中,

typeof foo

评估为'undefined'

但是

typeof (function() { return foo; })()

抛出错误:

ReferenceError: foo is not defined

这破坏了我对表达式可疑性的概念!到目前为止,我知道foo(function() { return foo; })()不相同的条件。

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


编辑:

另一个例子:

typeof (foo)
typeof (foo + 0)

我希望(foo)(foo + 0)会抛出错误。

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

5 个答案:

答案 0 :(得分:15)

基本上,typeof运算符检查变量¹是否不可解析并返回"undefined"。也就是说,typeof在达到GetValue算法之前返回未声明变量¹的定义值,该算法会抛出未声明的变量¹。

引用ECMAScript 5.1 § 11.4.3 The typeof Operator(强调补充):

  

11.4.3运营商类型

     

制作 UnaryExpression typeof UnaryExpression 是   评估如下:

     
      
  1. val 成为评估 UnaryExpression 的结果。
  2.   
  3. 如果Type val )为Reference,那么

         

    <强> 2.1。如果IsUnresolvableReference val )为true,请返回"undefined"

         

    2.2设val为GetValue val )。

  4.   
  5. 根据Type返回由Table 20 val )确定的字符串。

  6.   

另一方面,return statement - 像大多数读取标识符值的运算符和语句一样 - 将始终调用GetValue,它会抛出不可解析的标识符(未声明的变量)。引用ECMAScript 5.1 § 8.7.1 GetValue (V)(强调补充):

  

8.7.1 GetValue(V)

     
      
  1. 如果Type(V)不是参考,请返回V.
  2.   
  3. 让base成为调用GetBase(V)的结果。
  4.   
  5. 如果IsUnresolvableReference(V),则抛出ReferenceError例外。
  6.   

现在,分析代码:

typeof (function() { return foo; })()

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

因此,代码在评估IIFE return语句时抛出,然后才能评估typeof操作。

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

typeof (foo+1)

typeof之前评估添加。在GetValue开始播放foo之前,typeof调用typeof (foo) 时会出现错误。

现在:

GetValue

不抛出错误,因为Addition Operator(括号)本身没有“评估”任何东西,它只是强制优先。更具体地说,分组运算符不会调用delete。在上面的示例中,它返回一个(不可解析的)参考

grouping operator甚至添加了关于此的说明:

  

注意此算法不会将annotated ES5.1 spec应用于评估Expression的结果。这样做的主要动机是,typeoftypeof等运算符可以应用于带括号的表达式。


NB 我写了这个答案的重点是提供一个简单易懂的解释,将技术术语保持在最低限度,同时仍然足够清晰,并提供所要求的ECMAScript标准参考,我希望成为那些努力理解arguments运算符的开发人员的有用资源。

¹术语“变量”用于便于理解。更正确的术语是标识符,它不仅可以通过变量声明引入GetValue,还可以引入函数声明,形式参数,调用函数(with) ,catch / let阻止,将属性分配给全局对象,const和{{1}}语句(ES6),以及其他一些方法。

答案 1 :(得分:8)

  

这是标准行为吗?

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

  

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

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

修改

以下情况:

typeof (foo)

&#34;(&#34;和&#34;)&#34;是punctuators,表示分组,例如调用像foo(a, b)这样的函数时的(可能是空的)参数列表,或表示要评估的表达式,例如if (x < 0)等等。

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

以下情况:

typeof (foo + 0)

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

typeof foo + 0 // undefined0

由于运算符优先级:typeof foo返回字符串"undefined",因此+变为addition operator,因为其中一个参数是一个字符串,它会连接(字符串版本)另外,不是数学版本),因此0转换为字符串"0"并连接到"undefined",在字符串"undefined0"中重新开始。

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

typeof !foo 

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

答案 2 :(得分:2)

typeof没有抛出错误“ReferenceError:foo is not defined”,它被函数本身抛出。如果你曾经使用过:

typeof (function() { return 2; })()

它会按预期返回“数字”,但在此示例中,JavaScript甚至没有达到typeof正在运行的任何地方。您收到的错误与运行时相同:

function test () {
    return foo;
}
test();

答案 3 :(得分:2)

通过规范,我认为当有问题的运算符试图在其操作数上运行GetValue()时,这一切都归结为

typeof attempts to determine its operand's Type first. If that type is a Reference and is IsUnresolvableReference(), then it bails out and returns undefined.实质上,它没有完全评估操作数;如果确实如此,那么undefined的任何内容都会抛出异常,所以它会短路并返回一个漂亮有用的字符串。

在示例中,自执行函数和加法运算符调用GetValue 而不首先检查IsUnresolvableReference() typeof foothey call GetValue and throw an exception如果引用未解析(在我们的案例中undefined是{{1}})。 (我想!通过阅读规范,这是我最好的猜测。)

答案 4 :(得分:1)

这是标准行为。 typeof运算符几乎接受您传递给它的下一个变量的引用。

让我们试试typeof foo

javascript解释器查看typeof并找到foo的类型。

现在我们尝试typeof (function() { return foo })()

javascript解释器查看typeof。由于之后的表达式不是变量,因此它会计算表达式。 (function() { return foo })()会引发ReferenceError,因为foo未定义。如果可以传递varialbe的引用,例如(function() { return *foo })(),那么这不会发生。

注意:根据这一点,可能会认为typeof (foo)会抛出错误,因为(foo)不是变量而必须进行评估,但这是不正确的; typeof (foo)也将返回&#34; undefined&#34;如果foo没有定义。

本质上,解释器在&#34; safe&#34;中评估下一个变量,但不计算表达式。上下文,以便typeof不会抛出错误。

这有点令人困惑。