本文详细解析JavaScript中判断全局对象是否存在的10种常见写法,深入剖析var声明提升、ReferenceError错误原因以及typeof、in运算符和hasOwnProperty等方法的使用场景。通过对比不同写法的语义差异与跨平台兼容性,推荐在不确定对象状态时优先使用typeof判断,并强调始终使用var声明变量以避免运行时错误。
JavaScript对象存在性判断概述
JavaScript语言在设计上存在不够严谨之处,开发者稍有不慎便容易引发错误。
以下通过具体案例来说明这一现象。
假设我们需要判断一个名为myObj的全局对象是否已经存在,若不存在则进行声明。用自然语言描述该算法逻辑如下:
代码示例如下:
if (myObj不存在){
声明myObj;
} 你可能认为编写这段代码轻而易举,但实际上它涉及的语法问题远比表面复杂。正如Juriy Zaytsev所指出的,判断JavaScript对象是否存在的方法超过50种。只有深入理解JavaScript语言的具体实现细节,才能准确区分这些写法的差异。
第一种写法
基于var声明提升的写法
基于直觉,你可能会尝试以下写法:
代码示例如下:
if (!myObj) {
myObj = { };
} 然而,执行这段代码时,浏览器会直接抛出ReferenceError错误,导致程序中断。问题出在哪里?
原因在于,当if语句判断myObj是否为空时,该变量尚未定义,因此触发错误。修改为以下形式即可正常运行:
代码示例如下:
if (!myObj) {
var myObj = { };
} 为何加上var关键字后就不再报错?这是否意味着在if语句判断时,myObj已经存在?
要解答此疑问,需了解JavaScript解释器的工作机制。JavaScript遵循“先解析,后运行”的原则,在解析阶段即完成变量声明,因此上述代码实际等效于:
代码示例如下:
var myObj;
if (!myObj) {
var myObj = { };
} 由此可见,在if语句进行判断时,myObj确实已经存在,故不会报错。这体现了var命令的“代码提升”(hoisting)机制。JavaScript解释器仅提升由var定义的变量,对未使用var直接赋值的变量无效,这正是未加var会报错的根本原因。
第二种写法
基于顶层对象属性的写法
除了使用var,还有另一种改写方式也能得到正确结果:
代码示例如下:
if (!window.myObj) {
myObj = { };
} window是JavaScript的顶层对象,所有全局变量均为其属性。因此,判断myObj是否为空,等价于判断window对象是否拥有myObj属性,从而避免因myObj未定义而引发的ReferenceError。不过,从代码规范性角度考虑,建议对第二行也加上var:
代码示例如下:
if (!window.myObj) {
var myObj = { };
} 或者采用以下写法:
代码示例如下:
if (!window.myObj) {
window.myObj = { };
} 第三种写法
上述写法的缺点在于,在某些运行环境(如V8、Rhino)中,window未必是顶层对象。因此,建议改写为:
代码示例如下:
if (!this.myObj) {
this.myObj = { };
} 在全局变量层面,this关键字始终指向顶层对象,从而能够独立于不同的运行环境。
第四种写法
然而,上述写法可读性较差,且this指向可变,易出错。因此,可进一步改写为:
代码示例如下:
var global = this;
if (!global.myObj) {
global.myObj = { };
} 通过自定义变量global表示顶层对象,代码意图更加清晰。
第五种写法
基于typeof运算符的写法
此外,还可以使用typeof运算符来判断myObj是否已定义。
代码示例如下:
if (typeof myObj == "undefined") {
var myObj = { };
} 这是目前判断JavaScript对象是否存在的最广泛使用的方法。
第六种写法
由于在已定义但未赋值的情况下,myObj的值直接等于undefined,因此上述写法可简化为:
代码示例如下:
if (myObj == undefined) {
var myObj = { };
} 此处需注意两点:首先,第二行的var关键字不可省略,否则会出现ReferenceError错误;其次,undefined不能加单引号或双引号,因为这里比较的是undefined数据类型,而非字符串“undefined”。
第七种写法
上述写法在“精确比较”(===)的情况下依然成立:
代码示例如下:
if (myObj === undefined) {
var myObj = { };
} 第八种写法
其他判断方法与总结
根据JavaScript语言设计,undefined == null,因此比较myObj是否等于null也能得到正确结果:
代码示例如下:
if (myObj == null) {
var myObj = { };
} 尽管运行结果正确,但从语义角度看,这种判断方法是错误的,应尽量避免。因为null指已赋值为null的空对象(即对象实际有值),而undefined指不存在或未赋值的对象。因此,此处只能使用“比较运算符”(==),若使用“精确比较运算符”(===)则会出错。
第九种写法
还可以使用in运算符,判断myObj是否为顶层对象的一个属性:
代码示例如下:
if (!('myObj' in window)) {
window.myObj = { };
} 第十种写法
最后,使用hasOwnProperty方法,判断myObj是否为顶层对象的一个属性:
代码示例如下:
if (!this.hasOwnProperty('myObj')) {
this.myObj = { };
} 总结
1. 如果仅判断对象是否存在,推荐使用第五种写法。
2. 若除对象是否存在外,还需判断对象是否有null值,推荐使用第一种写法。
3. 除非特殊情况,所有变量都应使用var命令声明。
4. 为实现跨平台兼容,建议避免使用window表示顶层对象。
5. 在JavaScript语言中,null和undefined易混淆。在可能同时涉及两者的情况下,建议使用“精确比较”运算符(===)。