JavaScript心經-變量篇

来源:互联网 发布:广州文豆php培训学校 编辑:程序博客网 时间:2024/05/29 17:34

变量(Variables)

[Notes On 'Professional.Javascript.For.Web.Developer' - 1]


有三种常用的基本类型:boolean、number和string。还有两个特别一些的:undefined和null。

判断一个变量是否是undefined或者null

现在主要讨论undefined和null,因为一个程序的健壮性一部分就是取决于在操作之前对数据有效性的验证,其实也就是判断一个未知变量是否是undefined或者null状态。



NULL

null其实表示的是Object类型(Object是引用类型,所以上面没列出来,以后再说)的变量没有指向任何实际值,所以其实null是Object类型的。


在Firebug里面执行:
>>> typeof null 
"object"


注意,typeof是个一元操作符,它不是函数,不需要加括号,它返回的值是一个字符型变量。


再做另一个小实验:

>>> var null_object = null;   typeof null_object;
"object"


null值在被转换成布尔类型之后,理所当然变成false:
>>> !null_object; 
true


现在比较一下null和undefined:

>>> null == undefined
true
>>> null === undefined
false


这个结果是因为在'=='比较操作中,某一个的类型被自动转化了。



undefined


现在再来说说undefined,在Javascript里面,undefined就是一个独立的类型,一般的教科书的说法是:通常有两种情况会得出undefined值,一种是声明一个变量但始终没有赋值给它,另一种是声明一个函数但没有返回任何值。


执行下面的小实验:
>>> var declared_but_unassigned;  declared_but_unassigned == undefined;
true


如果把'=='换成是'===',得到的也会是true。


可是,上述作法对于一个根本就没有被声明过的变量来说,会抛出异常:
>>> non_exist === undefined;
ReferenceError: non_exist is not defined


对于这种情况,typeof操作符则可以安全地处理:

>>> typeof non_exist; 
"undefined"


所以说,如果你不能保证会不会去引用一个未声明变量的话,最稳妥的测试一个变量是否定义的写法应该是:

if(typeof some_variable === 'undefined'){// your code here.};

注意,是'===',而不是'==',这是很多人强调的,因为他们认为这样写可以避免一个特殊情况下会发生的误判,就是假如你之前执行过:var undefined = "something";这样的代码,但是实际上在眼下的Firefox(23.0.1版本)和Chrome(33.0.1750.154 m版本)里执行这句代码都不会导致undefined被重新赋值,因为ECMAScript 标准的第五版对于undefined的行为做了修正,undefined变成是不可修改的,所以客观地讲这个担心已经是多余的,不过既然这样写不会有坏处,那么就保持这样就好。


而有些人喜欢向其他高级语言那样使用!variable的写法,但是在Firebug里:
>>> !non_exist;  
ReferenceError: non_exist is not defined

其实得到的是一个引用错误。所以下面的写法其实并不适合于检测当一个变量定义或没定义时做什么:

if(non_exist){// your code here.}if(!non_exist){// your code here.}


现在咱们再来看看另一种情况,那就是并非在全局作用域通过var关键字定义的变量,而是一个Object类型变量的属性(property)。在Firebug里执行:
>>> var someObject = {prop1 : 'value1',prop2: 'value2'};  someObject.propUndef == undefined;
true

可见,直接引用一个没声明的属性却是不会抛出异常的。使用'==='得到的结果也是true。

>>> someObject.propUndef;
undefined
>>> !someObject.propUndef;
true
>>> someObject.propUndef == null;
true
>>> someObject.propUndef === null;
false
>>> if(!someObject.propUndef){console.log('here');}
here

所以说,对于对象类型上的属性,你却又是可以使用这种方式的:

if(someObject.someProperty){// your code here.}if(!someObject.someProperty){// your code here.}


对于数组,这个结论也同样适用:
>>> var list = []; list['CN'] = 'China'; list['US'] = 'the United States';
"the United States"

>>> list['FR'];
undefined
>>> list['FR'] == undefined;
true
>>> list['FR'] === undefined;
true
>>> list['FR'] == null;
true
>>> list['FR'] === null;
false
>>> if(!list['FR']){ console.log('here'); }
here


那么再回到全局变量,其实Javascript的运行环境中,window是顶层对象,也就是说所有的通过var和function声明的全局的变量或者函数,其实都是window对象的属性和方法。于是,从这个意义上,再重新做前面的实验:

>>> typeof window.non_exist;
"undefined"

>>> window.non_exist === undefined;
true

>>> ! window.non_exist;
true

>>> if(!window.non_exist){ console.log('here'); }
here


小结


那么在继续之前,稍稍总结一下目前得到的结论:


首先,在Javascript(在所有语言中都是如此)中去引用一个你根本没声明过的变量是一个很严重的错误,应尽量避免。可是事情并不总是如人意,有些时候一个程序或许由不同的人完成,那么在这一点无法保证的情况下,一个比较稳妥的写法来验证一个变量是否是未定义(就是说没声明,或者声明了但没赋值)的代码为:

if (typeof some_variable !== "undefined"){// your code here.};


你可能会纠结于用不用严格的比较操作符:'===','!=='。目前来讲,其实没有这个必要,但是既然使用严格的并没有什么害处,而且也能从逻辑上让验证更加严格,避免有些运行环境下可能发生的转换,那么就使用'==='和'!=='好了。


在实际情况中,一般来说仅仅判断一个变量是否未定义并没有多大帮助,通常来说,你至少还想要同时确定一个变量不是null,那么对于一个全局变量来说,一个完整且安全的写法应该是:

if (typeof some_variable !== "undefined" && some_variable !== null){// your code here.};

而对于一个对象上面的属性或者数组元素来说,你当然同样可以采用上面的做法,可是你还可以选择一种简化的做法:

if (someObject.someProperty != null){// your code here.};if (someArray['someKey'] != null){// your code here.};


在这个地方,是否应该使用严格的比较操作符是有争议的。有人引述JQuery编程风格指南里面的话:

Strict equality checks (===) should be used in favor of ==. The only exception is when checking for undefined and null by way of null.// Check for both undefined and null values, for some important reason. undefOrNull == null;


JQuery自己代码中的类似比较也都是使用'==',所以这里我觉得就不必再深究了,JQuery足以作为权威来参考。


所以,如果你不怕麻烦,即使全局的变量的验证,你也可以写成:

if (window.some_variable != null){// your code here.};


或者,如果你自己能保证你引用的变量一定都被声明过,那么你也就可以使用:

if (some_variable != null){// your code here.};


而在其他情况下,可能你想确保你的某个变量既不是未定义,也不是null,除此以外,它同时也不是像0,false和空字符串等等这样的一些逻辑非值。那么在你能确保这个变量被声明的前提下,你可以这样写:

if (some_variable){// your code here.};

如果你不确定,你又非要用一种简短方式写的话,下面这样也是可以:

if (window.some_variable){// your code here.};


当然,如果它本身就是一个对象上面的属性或者数组元素,那你可以不用有那么多顾虑了:

if (someObject.someProperty){// your code here.};if (someArray['someKey']){// your code here.};


那么,究竟哪些值,既不等于null或者undefined,又会在被隐性转换为布尔类型时变成false呢?
0
"" (空字符串)
false
NaN


这个可以通过实验来证明:
>>> null == "";
false
>>> NaN != null;
true


真实开发中的使用情况


一般来说,在真实的开发中,只有少数情况会直接使用上面的示例代码这种写法,比如检查一些第三方的函数库是否被加载,或者一些命名空间是否存在等等。而其他的情况相对会多一些,比如在一个函数的作用域内,对传进来的参数进行验证,和对一些其他函数的返回值进行这样的验证操作,多数情况下你可能会将这些返回值赋给一个局域变量,像这样做:

function a (){var someVar = someFun(); // check someVar here depends on your requirements.};

但是究竟具体应该怎么写,一来取决于你对于这个变量的类型的预期,比如可能你期望它返回的是一个数字(number类型);同时也取决于你对函数someFun()的了解,可能你已经确保它无论如何也不会返回字符串(string)或者布尔(boolean)类型,这样的情况下,你确保这个变量不是undefined,不是null,也不是NaN就行了。所以你可以这样写:

function a (){var someVar = someFun(); if (typeof someVar !== "undefined" && someVar != null && !isNaN(someVar)){// your code here.}};

但是实际上,因为你已经声明了someVar,所以它不会抛出引用异常;同时,因为你也真的确保someFun()不会返回string或者boolean类型,所以可以不去管false或者空字符串的可能;另外,你期待的数值是一个非0的数值的话,那么你可以将其简化为:

function a (){var someVar = someFun(); if (someVar){// your code here.}};

最后来找一些真实的情况来看看如何应用上面这一大堆实验结论,随便找个域名打开一个网站:http://www.douban.com/people/vincent.zhang/,打开Firebug,在里面执行:
>>> elems = document.getElementById('vince');
null
所以可见,在找不到相应元素的情况下,getElementById函数返回的是一个null值的对象类型变量。接下来执行:
>>> elems == null;
true


>>> if (!elems){  console.log('here');  }
here


现在再来看看JQuery提供的一个函数attr()。


情况1:


执行:
>>> var className = $('div#db-global-nav').attr('class');
undefined
>>> className;
"global-nav"
>>> if (className) { console.log('here'); }
here


这说明有一个ID为db-global-nav的DIV元素,它有class属性,并且值是global-nav,这个没什么好说的。


情况2:


现在咱们来找一个没有class属性的元素的class值:
>>> var className = $('div#wrapper').attr('class');
undefined
>>> className;
""
>>> if (className == '') { console.log('here'); }
here
>>> if (typeof className !== 'undefined' && className != null) { console.log('here'); }
here
>>> if (className) { console.log('here'); }
undefined
>>> if (!className) { console.log('here'); }
here


可见,在第一句被执行之后,className被赋值为一个空字符串。


情况3:


现在再来看看另一种情况,就是给$()传递一个不存在的ID:
>>> var className = $('div#vince').attr('class');
undefined
>>> className;
undefined
>>> typeof className;
"undefined"
>>> if (typeof className !== 'undefined' && className != null) { console.log('here'); }
undefined
>>> if (className) { console.log('here'); }
undefined
>>> if (!className) { console.log('here'); }
here


所以说,attr()函数返回的可能是一个字符串,可能是一个空字符串,也可能是undefined,没有其他可能了。那么常规做法,可以是:

var className = $('div#vince').attr('class');if (typeof className !== 'undefined' && className !== ''){// your code here.};

这其实就做到你想做的了。可是,由于我们声明了一个变量className,并把返回的值赋给它,所以引用className永远不会抛出引用异常,也就是说将它直接放在if表达式里是安全的:

var className = $('div#vince').attr('class');if (className){// your code here.};


而有些人发现下面的写法也是能够达到目的的:

var className = $('div#vince').attr('class');if (className != null && className !== ''){// your code here.};

这是因为,undefined被认为等于null,所以前半部分判断可以捕获attr()返回undefined的情况。


扩展与延伸


这个部分可以看做是为了完整性而对相关话题的一些补充。其实Javascript给验证对象上的属性和数组的元素提供了相应的方法,就是in操作符,和hasOwnProperty()方法:


>>> var someObject = {prop1 : 'value1',prop2: 'value2'};
undefined
>>> 'prop2' in someObject;
true
>>> 'prop3' in someObject;
false
>>> someObject.hasOwnProperty('prop2');    
true
>>> someObject.hasOwnProperty('prop3');    
false




>>> var list = []; list['CN'] = 'China'; list['US'] = 'the United States';
"the United States"
>>> 'CN' in list; 
true
>>> 'FR' in list; 
false
>>> list.hasOwnProperty('CN'); 
true
>>> list.hasOwnProperty('FR'); 
false


而上述方法按说应该是判断一个对象是否具有某个属性或者一个数组里是否含有某个键值的正规方法。





参考:

How to determine if variable is 'undefined' or 'null'
How to check for an undefined or null variable in JavaScript?
JavaScript: how to test if a variable is not NULL
How to check if a variable is both null and /or undefined in javascript
Detecting an undefined object property in JavaScript
How to check for “undefined” in JavaScript?



var的作用




判定变量的类型




变量间的比较与转换


>>> 0 == "";
true








作用域(Scope)与闭包(Closure)



原型(Prototype)



未完待續……

0 0
原创粉丝点击