JavaScript:undefined不是字面量

今天看了一下《高性能JavaScript》,发现一个问题,就是书中把undefined也当成了字面量(直接量),这是不对的.

中文版:

英文原版:

Literal values

Any value that represents just itself and isn’t stored in a particular location. JavaScript can represent strings, numbers, Booleans, objects, arrays, functions, regular expressions, and the special values null and undefined as literals.

什么是字面量

我想,这个名词很难去定义,因为ES规范上也没有明确给出到底什么是字面量.维基百科上是这么定义的:

一个字面量就是在源代码中表示某个固定值的符号.

我的理解是:编译器或者解释器看到一个字面量,就知道它表示的是哪个具体的值.比如我们把符号1规定为值1的字面量,这只是规定,如果把符号一规定成是值1的字面量,那么一就成了字面量.再比如字符串字面量,常见的表示法是用引号"string",但在perl中,q/string/也是一个字符串字面量.也就是说,,每种语言的字面量有不同的符号表示法.字面量和标识符是冲突的,这也就是为什么标识符不能以数字开头,a1可以是标识符,11可就不行了.null是字面量(同时也符合标识符的语法),所以得把它规定成为保留字,不能作为标识符使用.

undefined不是字面量

书中说特殊值undefined也是字面量,这是不对的.我们可以在ES5规范中找一下,只能找到下面这些字面量语法:

ArrayLiteral
BooleanLiteral
DecimalIntegerLiteral
DecimalLiteral
HexIntegerLiteral
JSONBooleanLiteral
JSONNullLiteral
NullLiteral
NumericLiteral
ObjectLiteral
OctalIntegerLiteral
RegularExpressionLiteral
StrDecimalLiteral
StringLiteral
StringNumericLiteral
StrNumericLiteral
StrUnsignedDecimalLiteral

你可以看到,这里面有数组字面量,布尔值字面量,十进制整数字面量等,就是没有undefined字面量.为什么呢,那我们写在JavaScript源代码中的undefined到底是什么.

undefined是什么

ES5规范中讲了:

8.1 Undefined类型

Undefined类型只有一个值,叫做undefined.任何没有被赋过值的变量的值为undefined.

Undefined是种类型,就像Number类型一样.Number类型有无数个值,比如0,1,3.14等等.String类型也有无数个值,比如"123","abc"等等,Boolean类型有两个值true和false.而Undefined类型只有一个值,就是undefined.类似的还有Null类型,它也只有一个值null.

ReservedWord ::                 //保留字包括下面的几种:
Keyword                         //关键字
FutureReservedWord              //未来保留字
NullLiteral                     //null字面量
BooleanLiteral                  //布尔值字面量,也就是true和false

null是个保留字,你不能把null作为变量名,单独的null(不被包含在字符串或正则字面量中)在JavaScript代码中的意思就是null值.而undefined不是保留字,你可以定义一个名为undefined的局部变量.而且JavaScript引擎已经内置了一个undefined全局变量,它的值是undefined.

15.1.1 全局对象的属性

15.1.1.1 NaN

NaN的值是NaN (see 8.5).

15.1.1.2 Infinity

Infinity的值是+∞ (see 8.5). 

15.1.1.3 undefined

undefined的值是undefined (see 8.1).

更明确点讲,我们写在JavaScript代码中的undefined,并不是undefined值本身.而是一个局部变量或者是全局对象的一个属性.但大部分时候它们的值是undefined.NaN和Infinity也同理.

 有什么影响

 维基百科上有一段话讲了undefined的这个特点:

注意:根本不存在undefined字面量.因此(x == undefined)并不能说明变量x的值就是undefined,因为在ECMAScript 5之前的规范中,可以使用语句var undefined = "I'm defined now"来修改undefined的值.更可靠的检测方法是(typeof x === 'undefined').

现在有不少人在自己的代码中使用了jQuery的这种写法,为什么要这么写呢.

;(function(window,undefined){

})(window)

其中一个原因就是防止全局的undefined变量被修改成其他值,但随着ES5的普及以及几乎不可能有人去改这个值,我觉得这种考虑越来越多余.这种写法还有其他两个考虑,一个是避免查找作用域链(这种性能差异真的值得考虑吗?),还有就是为了代码压缩了.

证明

我们可以使用Ariya Hidayat(phantomjs的作者)写的基于Esprima(纯JavaScript编写)ECMAScript解析器来解析一下这几个符号

Infinity 
undefined
NaN
null 100

解析得到的语法树是:

{
"type":"Program",
"body":[{
"type":"ExpressionStatement",
"expression":{
"type":"Identifier",
"name":"Infinity" } }, { "type":"ExpressionStatement",
"expression":{
"type":"Identifier",
"name":"undefined" } }, { "type":"ExpressionStatement",
"expression":{
"type":"Identifier",
"name":"NaN" } }, { "type":"ExpressionStatement",
"expression":{
"type":"Literal",
"value":null,
"raw":"null" } }, { "type":"ExpressionStatement",
"expression":{
"type":"Literal",
"value":100,
"raw":"100" } } ] }

可以看到,前三个名称都是标识符,后两个才是字面量.如果你还不放心,可以试试SpiderMonkey自己的解析器,运行结果如下:

js> Reflect.parse('undefined').body[0].expression.type
"Identifier" js> Reflect.parse('null').body[0].expression.type
"Literal"
posted @ 2012-11-11 20:15  紫云飞  阅读(2983)  评论(4编辑  收藏  举报