JavaScript精进篇
JavaScript是所有前端框架中最基础的框架,在工作了两年以后又重新回到了这里。过去两年里用的最多的前端框架是jquery,因为它简单易上手,而jquery就是封装了JavaScript。重新系统的回顾一下JavaScript,你能了解到很多以前不知道的小知识、忽略的小细节,夯实基础。可能了解的这些在编码中用到的很少或者基本用不到,但是却能够加深理解,而且会有一种茅塞顿开的感觉,还不时会遇到以前不理解但是匆匆掠过的问题,看完了以后会说一句“原来如此”,使技术精进。相信重新回顾一下不仅会打牢基础,而且对以后学习别的前端框架会起到事半功倍的作用。
1、CData
在HTML 中,有特殊的规则用以确定<script>元素中的哪些内容可以被解析,但这些特殊的规则在XHTML 中不适用。这里比较语句a < b 中的小于号(<)在XHTML 中将被当作开始一个新标签来解析。但是作为标签来讲,小于号后面不能跟空格,因此就会导致语法错误。
避免在XHTML 中出现类似语法错误的方法有两个。一是用相应的HTML 实体(<)替换代码中所有的小于号(<),替换后的代码类似如下所示:
<script type="text/javascript"> function compare(a, b) { if (a < b) { alert("A is less than B"); } else if (a > b) { alert("A is greater than B"); } else { alert("A is equal to B"); } } </script>
虽然这样可以让代码在XHTML 中正常运行,但却导致代码不好理解了。为此,我们可以考虑采用另一个方法。
保证让相同代码在XHTML 中正常运行的第二个方法,就是用一个CData 片段来包含JavaScript 代码。在XHTML(XML)中,CData 片段是文档中的一个特殊区域,这个区域中可以包含不需要解析的任意格式的文本内容。因此,在CData 片段中就可以使用任意字符——小于号当然也没有问题,而且不会导致语法错误。引入CData 片段后的JavaScript 代码块如下所示:
<script type="text/javascript"><![CDATA[ function compare(a, b) { if (a < b) { alert("A is less than B"); } else if (a > b) { alert("A is greater than B"); } else { alert("A is equal to B"); } } ]]></script>
在兼容XHTML 的浏览器中,这个方法可以解决问题。但实际上,还有不少浏览器不兼容XHTML,因而不支持CData 片段。怎么办呢?再使用JavaScript 注释将CData 标记注释掉就可以了:
<script type="text/javascript"> //<![CDATA[ function compare(a, b) { if (a < b) { alert("A is less than B"); } else if (a > b) { alert("A is greater than B"); } else { alert("A is equal to B"); } } //]]> </script>
2、var 局部变量与全局变量
用var 操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用var 定义一个变量,那么这个变量在函数退出后就会被销毁,例如:
function test(){ var message = "hi"; // 局部变量 } test(); alert(message); // 错误!
这里,变量message 是在函数中使用var 定义的。当函数被调用时,就会创建该变量并为其赋值。而在此之后,这个变量又会立即被销毁,因此例子中的下一行代码就会导致错误。不过,可以像下面这样省略var 操作符,从而创建一个全局变量:
function test(){ message = "hi"; // 全局变量 } test(); alert(message); // "hi"
这个例子省略了var 操作符,因而message 就成了全局变量。这样,只要调用过一次test()函数,这个变量就有了定义,就可以在函数外部的任何地方被访问到。
注意:虽然省略var 操作符可以定义全局变量,但这也不是我们推荐的做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了var 操作符,也会由于相应
变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致抛出ReferenceError 错误。
3、null == undefined
首先 alert(null == undefined); 返回的值是 true。
接下来分别解释一下这两个类型
Undefined 类型只有一个值,即特殊的undefined。在使用var 声明变量但未对其加以初始化时,这个变量的值就是undefined,例如:
var message; alert(message == undefined); //true
这个例子只声明了变量message,但未对其进行初始化。比较这个变量与undefined 字面量,结果表明它们是相等的。
不过,包含undefined 值的变量与尚未定义的变量还是不一样的。看看下面这个例子:
var message; // 这个变量声明之后默认取得了undefined 值 // 下面这个变量并没有声明 // var age alert(message); // "undefined" alert(age); // 产生错误
运行以上代码,第一个警告框会显示变量message 的值,即"undefined"。而第二个警告框——由于传递给alert()函数的是尚未声明的变量age——则会导致一个错误。对于尚未声明过的变量,只能执行一项操作,即使用typeof 操作符检测其数据类型(对未经声明的变量调用delete 不会导致错误,但这样做没什么实际意义,而且在严格模式下确实会导致错误)。
然而,令人困惑的是:对未初始化的变量执行typeof 操作符会返回undefined 值,而对未声明的变量执行typeof 操作符同样也会返回undefined 值。来看下面的例子:
var message; // 这个变量声明之后默认取得了undefined 值 // 下面这个变量并没有声明 // var age alert(typeof message); // "undefined" alert(typeof age); // "undefined"
结果表明,对未初始化和未声明的变量执行typeof 操作符都返回了undefined 值;这个结果有其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。
结论:即便未初始化的变量会自动被赋予undefined 值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当typeof 操作符返回"undefined"值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。
Null 类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用typeof 操作符检测null 值时会返回"object"的原因,如下面的例子所示:
var car = null; alert(typeof car); // "object"
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null 而不是其他值。这样一来,只要直接检查null 值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子所示:
if (car != null){ // 对car 对象执行某些操作 }
实际上,undefined 值是派生自null 值的,因此ECMA-262 规定对它们的相等性测试要返回true:
alert(null == undefined); //true
这里,位于null 和undefined 之间的相等操作符(==)总是返回true,不过要注意的是,这个操作符出于比较的目的会转换其操作数。
尽管null 和undefined 有这样的关系,但它们的用途完全不同。如前所述,无论在什么情况下都没有必要把一个变量的值显式地设置为undefined,可是同样的规则对null 却不
适用。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null 值。这样做不仅可以体现null 作为空对象指针的惯例,而且也有助于进一步
区分null 和undefined。
4、Boolean和Boolean()
Boolean 类型是ECMAScript 中使用得最多的一种类型,该类型只有两个字面值:true 和false。这两个值与数字值不是一回事,因此true 不一定等于1,而false 也不一定等于
0。
虽然Boolean 类型的字面值只有两个,但ECMAScript 中所有类型的值都有与这两个Boolean 值等价的值。要将一个值转换为其对应的Boolean 值,可以调用转型函数
Boolean(),如下例所示:
var message = "Hello world!"; var messageAsBoolean = Boolean(message);
在这个例子中,字符串message 被转换成了一个Boolean 值,该值被保存在messageAsBoolean变量中。可以对任何数据类型的值调用Boolean()函数,而且总会返回一个
Boolean 值。至于返回的这个值是true 还是false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则。
这些转换规则对理解流控制语句(如if 语句)自动执行相应的Boolean 转换非常重要,请看下面的代码:
var message = "Hello world!"; if (message){ alert("Value is true"); }
运行这个示例,就会显示一个警告框,因为字符串message 被自动转换成了对应的Boolean 值(true)。由于存在这种自动执行的Boolean 转换,因此确切地知道在流控制语
句中使用的是什么变量至关重要。错误地使用一个对象而不是一个Boolean 值,就有可能彻底改变应用程序的流程。