判断一个Javascript对象是否存在的方法有很多,然而只有对Javascript语言的实现细节非常清楚,才可能分得清它们的区别。下面我们一起来了解一下判断JavaScript对象是否存在的10个方法。! \/ A0 @& s( d. _
Javascript语言的设计不够严谨,很多地方一不小心就会出错。
: z6 s x2 d w5 g" f9 x; e7 ]; A 举例来说,请考虑以下情况。
e5 N- C: S: [- i* J) v 现在,我们要判断一个全局对象myObj是否存在,如果不存在,就对它进行声明。用自然语言描述的算法如下:6 t/ s3 u; H, n: X# o, G
if (myObj不存在){ 声明myObj; }$ b* `, D4 K& Z/ W: j# h
你可能会觉得,写出这段代码很容易。但是实际上,它涉及的语法问题,远比我们想象的复杂。Juriy Zaytsev指出,判断一个Javascript对象是否存在,有超过50种写法。只有对Javascript语言的实现细节非常清楚,才可能分得清它们的区别。
. `1 Z6 l" L) L: K7 J& I 第一种写法: s ]9 i, ^+ ^6 A" k$ V
根据直觉,你可能觉得可以这样写:
. A! P; r8 H9 L2 _ if (!myObj)& Z# G& e/ _# _6 K) F
{ myObj = { }; }- k, d% s$ `: m
但是,运行这段代码,浏览器会直接抛出ReferenceError错误,导致运行中断。请问错在哪里? y& _6 k2 T3 ~. l& Z3 [
对了,if语句判断myObj是否为空时,这个变量还不存在,所以才会报错。改成下面这样,就能正确运行了。
% x, \7 p2 H: ~2 Z if (!myObj)3 x" E8 o+ h9 B, [; X) u; z
{ var myObj = { }; }
& L! O2 N" _* [3 @% j# q 为什么加了一个var以后,就不报错了?难道这种情况下,if语句做判断时,myObj就已经存在了吗?) V, V4 l# x& R5 v! L( V w
要回答这个问题,就必须知道Javascript解释器的工作方式。Javascript语言是"先解析,后运行",解析时就已经完成了变量声明,所以上面的代码实际等同于:% t$ ?1 g* W& x y! H% h
var myObj; if (!myObj)
8 \& |9 J+ m3 [ { var myObj = { }; }
; m6 s/ v$ ]; y* ]6 V 因此,if语句做判断时,myObj确实已经存在了,所以就不报错了。这就是var命令的"代码提升"(hoisting)作用。Javascript解释器,只"提升"var命令定义的变量,对不使用var命令、直接赋值的变量不起作用,这就是为什么不加var会报错的原因。5 h1 L' L0 V. b) b
第二种写法# B# X0 S2 i' ~9 V5 ~- p
除了var命令,还可以有另一种改写,也能得到正确的结果:
6 u( }7 C+ b; l" m% K3 z- U if (!window.myObj)( ^" b1 J _8 K! H& A+ e0 S& O
{ myObj = { }; }6 o& r/ {- C G. _
window是javascript的顶层对象,所有的全局变量都是它的属性。所以,判断myobj是否为空,等同于判断window对象是否有myobj属性,这样就可以避免因为myObj没有定义而出现ReferenceError错误。不过,从代码的规范性考虑,最好还是对第二行加上var:
& W& A+ A) `# ^ if (!window.myObj)3 k3 U2 k" U' b4 j& S/ a
{ var myObj = { }; }
9 d. N9 x1 \$ _9 z' \, v2 V 或者写成这样:
; G: y e& ?3 @4 k) ? if (!window.myObj)( `/ M0 r9 w- r* L# I
{ window.myObj = { }; }
8 V2 u# W2 e( _) e0 h, V 第三种写法& e8 g, r" v' ~" g
上面这种写法的缺点在于,在某些运行环境中(比如V8、Rhino),window未必是顶层对象。所以,考虑改写成:
" b/ `, `* v4 w2 Y& t5 b; t2 G if (!this.myObj) ^3 j% U/ C _
{ this.myObj = { }; }- Y0 p2 S" z& \5 ~2 B
在全局变量的层面中,this关键字总是指向顶层变量,所以就可以独立于不同的运行环境。
: {4 t3 u; N& Z2 C$ T5 V4 s% `/ C 第四种写法
8 s+ J `" B4 ^5 G- H4 K 但是,上面这样写可读性较差,而且this的指向是可变的,容易出错,所以进一步改写:* j( V i8 x! w9 b+ F3 g
var global = this; if (!global.myObj)
: x- G* F% M2 v6 d+ b { global.myObj = { }; }, ?) U) ]. ?/ ^+ o, N9 d# a# \* K
用自定义变量global表示顶层对象,就清楚多了。
7 ]& _4 I" Z" T; A0 |& @ 第五种写法) V6 T5 p* {* x4 Q2 V. B
还可以使用typeof运算符,判断myObj是否有定义。! |& M3 @6 s' R3 K
if (typeof myObj == "undefined")
- L2 B% p" [- w" G { var myObj = { }; }
* o2 |0 t0 l# t4 N% p% z8 ~, t 这是目前使用最广泛的判断javascript对象是否存在的方法。
+ v( b" D* v" G( ^( u9 M5 c& G 第六种写法
1 i) k4 Y; s% | 由于在已定义、但未赋值的情况下,myObj的值直接等于undefined,所以上面的写法可以简化:
6 G$ H! B& G9 F4 A0 D& n9 w if (myObj == undefined) O" J/ i5 T, n2 U% X7 L) k9 c
{ var myObj = { }; } |