1.延迟脚本
HTML 4.01 为 <script> 标签定义了 defer 属性。这个属性的用途是表明脚本在执行时不会影响页
面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在 <script> 元素中设置
defer 属性,相当于告诉浏览器立即下载,但延迟执行。
<!DOCTYPE html>
<html>
<head>
<title>我的HTML</title>
<script type="text/javascript" defer="defer"src="example1.js"></script>
<script type="text/javascript" defer="defer"src="example2.js"></script>
</head>
<body>
<!-- 这里放内容 -->
</body>
</html>
在这个例子中,虽然我们把<script> 元素放在了文档的<head> 元素中,但其中包含的脚本将延迟
到浏览器遇到</html> 标签后再执行。HTML5 规范要求脚本按照它们出现的先后顺序执行,因此第一
个延迟脚本会先于第二个延迟脚本执行, 而这两个脚本会先于 DOMContentLoaded 事件 (详见第 13 章)
执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在 DOMContentLoaded 事件触发
前执行,因此最好只包含一个延迟脚本。
前面提到过, defer 属性只适用于外部脚本文件。这一点在 HTML5 中已经明确规定,因此支持
HTML5 的实现会忽略给嵌入脚本设置的 defer 属性。IE4~IE7 还支持对嵌入脚本的 defer 属性,但
IE8 及之后版本则完全支持 HTML5 规定的行为。
2.异步脚本
HTML5 为 <script> 元素定义了 async 属性。这个属性与 defer 属性类似,都用于改变处理脚本的行为。同样与 defer 类似, async 只适用于外部脚本文件, 并告诉浏览器立即下载文件。但与 defer
不同的是,标记为 async的脚本并不保证按照指定它们的先后顺序执行。例如:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script type="text/javascript" asyncsrc="example1.js"></script>
<script type="text/javascript" asyncsrc="example2.js"></script>
</head>
<body>
<!-- 这里放内容 -->
</body>
</html>
在以上代码中,第二个脚本文件可能会在第一个脚本文件之前执行。因此,确保两者之间互不依赖
非常重要。指定 async 属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。
为此,建议异步脚本不要在加载期间修改 DOM。
异步脚本一定会在页面的load 事件前执行,但可能会在DOMContentLoaded 事件触发之前或之
后执行。支持异步脚本的浏览器有 Firefox 3.6、Safari 5 和 Chrome。
3. 在 HTML 中,有特殊的规则用以确定 <script> 元素中的哪些内容可以被解析,但这些特殊的规则
在 XHTML 中不适用。这里比较语句 a < b 中的小于号(<)在 XHTML中将被当作开始一个新标签来
解析。但是作为标签来讲,小于号后面不能跟空格,因此就会导致语法错误。
——————————
① HTML5 正快速地被前端开发人员采用,建议读者在学习和开发中遵循 HTML5 标准,本节内容可以跳过。避免在 XHTML 中出现类似语法错误的方法有两个。一是用相应的 HTML 实体( < )替换代码
中所有的小于号(<) ,替换后的代码类似如下所示:
<scripttype="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 代码块如下所示:
<scripttype="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 标记注释掉就可以了:
<scripttype="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>
这种格式在所有现代浏览器中都可以正常使用。虽然有几分 hack 的味道,但它能通过XHTML 验
证,而且对 XHTML 之前的浏览器也会平稳退化。
小结
把 JavaScript 插入到 HTML 页面中要使用 <script> 元素。使用这个元素可以把 JavaScript 嵌入到
HTML页面中,让脚本与标记混合在一起;也可以包含外部的 JavaScript文件。而我们需要注意的地方有:
在包含外部 JavaScript 文件时,必须将src 属性设置为指向相应文件的 URL。而这个文件既可
以是与包含它的页面位于同一个服务器上的文件,也可以是其他任何域中的文件。
所有 <script> 元素都会按照它们在页面中出现的先后顺序依次被解析。在不使用 defer 和
async 属性的情况下,只有在解析完前面 <script> 元素中的代码之后,才会开始解析后面
<script> 元素中的代码。
由于浏览器会先解析完不使用 defer 属性的<script> 元素中的代码,然后再解析后面的内容,
所以一般应该把 <script> 元素放在页面最后,即主要内容后面,</body> 标签前面。
使用 defer 属性可以让脚本在文档完全呈现之后再执行。延迟脚本总是按照指定它们的顺序执行。
使用 async 属性可以表示当前脚本不必等待其他脚本,也不必阻塞文档呈现。不能保证异步脚
本按照它们在页面中出现的顺序执行。
另外,使用 <noscript> 元素可以指定在不支持脚本的浏览器中显示的替代内容。但在启用了脚本
的情况下,浏览器不会显示 <noscript> 元素中的任何内容
5. typeof 操作符
鉴于 ECMAScript 是松散类型的,因此需要有一种手段来检测给定变量的数据类型——typeof 就
是负责提供这方面信息的操作符。对一个值使用 typeof 操作符可能返回下列某个字符串:
"undefined" ——如果这个值未定义;
"boolean" ——如果这个值是布尔值;
"string" ——如果这个值是字符串;
"number" ——如果这个值是数值;
"object" ——如果这个值是对象或 null ;
"function" ——如果这个值是函数。
下面是几个使用 typeof操作符的例子:
var message = "some string";
alert(typeof message); //"string"
alert(typeof(message)); //"string"
alert(typeof 95); // "number"
TypeofExample01.htm
这几个例子说明, typeof操作符的操作数可以是变量(message ) ,也可以是数值字面量。注意,
typeof 是一个操作符而不是函数,因此例子中的圆括号尽管可以使用,但不是必需的。
有些时候, typeof 操作符会返回一些令人迷惑但技术上却正确的值。比如,调用 typeofnull
会返回"object" ,因为特殊值 null 被认为是一个空的对象引用。Safari5 及之前版本、Chrome 7及之
前版本在对正则表达式调用typeof 操作符时会返回"function" ,而其他浏览器在这种情况下会返回
"object" 。
6. 实际上,如果指定了 16 作为第二个参数,字符串可以不带前面的 "0x" ,如下所示:
var num1 = parseInt("AF", 16);//175
var num2 = parseInt("AF"); //NaN
这个例子中的第一个转换成功了,而第二个则失败了。差别在于第一个转换传入了基数,明确告诉
parseInt() 要解析一个十六进制格式的字符串;而第二个转换发现第一个字符不是数字字符,因此就
自动终止了。
指定基数会影响到转换的输出结果。例如:
var num1 = parseInt("10", 2); //2(按二进制解析)
var num2 = parseInt("10", 8); //8(按八进制解析)
var num3 = parseInt("10", 10);//10 (按十进制解析)
var num4 = parseInt("10", 16);//16 (按十六进制解析)
NumberExample07.htm
不指定基数意味着让parseInt() 决定如何解析输入的字符串,因此为了避免错误的解析,我们建议无论在什么情况下都明确指定基数。
与 parseInt() 函数类似, parseFloat() 也是从第一个字符(位置 0)开始解析每个字符。而且
也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第
一个小数点是有效的,而第二个小数点就是无效的了,因此它后面的字符串将被忽略。举例来说,
"22.34.5"将会被转换为 22.34。
除了第一个小数点有效之外,parseFloat() 与 parseInt() 的第二个区别在于它始终都会忽略前导
的零。parseFloat() 可以识别前面讨论过的所有浮点数值格式,也包括十进制整数格式。但十六进制格
式的字符串则始终会被转换成0。由于parseFloat() 只解析十进制值,因此它没有用第二个参数指定基
数的用法。最后还要注意一点:如果字符串包含的是一个可解析为整数的数(没有小数点,或者小数点后
都是零) , parseFloat() 会返回整数。以下是使用 parseFloat() 转换数值的几个典型示例。
var num1 =parseFloat("1234blue"); //1234 (整数)
var num2 = parseFloat("0xA"); //0
var num3 = parseFloat("22.5");//22.5
var num4 = parseFloat("22.34.5");//22.34
var num5 = parseFloat("0908.5");//908.5
var num6 = parseFloat("3.125e7");//31250000
7. toString() 可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格
式表示的字符串值。下面给出几个例子:
var num = 10;
alert(num.toString()); // "10"
alert(num.toString(2)); // "1010"
alert(num.toString(8)); // "12"
alert(num.toString(10)); // "10"
alert(num.toString(16)); // "a"
在不知道要转换的值是不是 null 或 undefined 的情况下,还可以使用转型函数 String() ,这个
函数能够将任何类型的值转换为字符串。 String() 函数遵循下列转换规则:
如果值有 toString() 方法,则调用该方法(没有参数)并返回相应的结果;
如果值是 null ,则返回 "null" ;
如果值是 undefined ,则返回 "undefined" 。
下面再看几个例子:
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); // "10"
alert(String(value2)); // "true"
alert(String(value3)); // "null"
alert(String(value4)); //"undefined"
8.
var o = new Object(); 推荐
var o = new Object; // 有效,但不推荐省略圆括号
Object 的每个实例都具有下列属性和方法。
constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)
就是 Object() 。
hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例
的原型中)是否存在。其中,作为参数的属性名( propertyName )必须以字符串形式指定(例
如: o.hasOwnProperty("name") )。
isPrototypeOf(object) :用于检查传入的对象是否是传入对象的原型(第 5 章将讨论原
型) 。
propertyIsEnumerable(propertyName) :用于检查给定的属性是否能够使用 for-in 语句
(本章后面将会讨论)来枚举。与 hasOwnProperty() 方法一样,作为参数的属性名必须以字符
串形式指定。
toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
toString() :返回对象的字符串表示。
valueOf() :返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值
相同
9.
var gae = 1;
var haha = gae++; // 先赋值 ,后自加
console.log(haha); // 1
var gae = 1;
gae++;
console.log(gae) // 2
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2;// 等于 21
var num4 = num1 + num2;// 等于 21
var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2;// 等于 22 先参与运算后自减
var num4 = num1 + num2;// 等于 21
var num1 = true; //在应用于布尔值 true 时,先将其转换为1 再执行加减 1 的操作。布尔值变量变成数值变量
num1++;
console.log(num1) // 2
var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
在应用于对象时,先调用对象的 valueOf() 方法(第 5 章将详细讨论)以取得一个可供操作的
值。然后对该值应用前述规则。如果结果是 NaN ,则在调用 toString() 方法后再应用前述规
则。对象变量变成数值变量
var o = {
valueOf: function() {
return -1;
}
};
s1++; // 值变成数值 3
s2++; // 值变成 NaN
b++; // 值变成数值 1
f--; // 值变成0.10000000000000009(由于浮点舍入错误所致)
o--; // 值变成数值-2
+ 一元操作符
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = +s1; // 值变成数值 1
s2 = +s2; // 值变成数值1.1
s3 = +s3; // 值变成 NaN
b = +b; // 值变成数值 0
f = +f; // 值未变,仍然是1.1
o = +o; // 值变成数值-1
- 一元操作符
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = -s1; // 值变成了数值-1
s2 = -s2; // 值变成了数值-1.1
s3 = -s3; // 值变成了 NaN
b = -b; // 值变成了数值 0
f = -f; // 变成了-1.1
o = -o; // 值变成了数值 1
二进制转换
alert(!false); // true
alert(!"blue");// false
alert(!0); // true
alert(!NaN);// true
alert(!""); //true
alert(!12345); // false
alert(!!"blue");//true
alert(!!0); //false
alert(!!NaN);//false
alert(!!"");//false
alert(!!12345); //true
&& 要是true 都是true 如果都是false 都是false
var found = true;
var result = (found&& someUndefinedVariable); // 这里会发生错误
alert(result); // 这一行不会执行
LogicalAndExample01.htm
在上面的代码中,当执行逻辑与操作时会发生错误,因为变量 someUndefinedVariable 没有声
明。由于变量 found 的值是 true ,所以逻辑与操作符会继续对变量 someUndefinedVariable 求值。
但 someUndefinedVariable 尚未定义,因此就会导致错误。这说明不能在逻辑与操作中使用未定义
的值。如果像下面这个例中一样,将 found 的值设置为 false ,就不会发生错误了:
var found = false;
var result = (found&& someUndefinedVariable); // 不会发生错误
alert(result); // 会执行("false")
var found = false;
var result = (found&& someUndefinedVariable); // 不会发生错误
if (found &&someUndefinedVariable) { // 这里为 false
alert(result); // 不会执行
}
alert(result); //false 这里会执行
逻辑或操作符由两个竖线符号( || )表示,有两个操作数
var found = true;
var result = (found ||someUndefinedVariable); // 不会发生错误
alert(result); // 会执行("true")
3.6.2 do-while 语句
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。
换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的
语法:
do {
statement
} while (expression);
下面是一个示例:
var i = 0;
do {
i += 2;
} while (i < 10);
alert(i);
while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。
因此,循环体内的代码有可能永远不会被执行。以下是 while 语句的语法:
while(expression)statement
下面是一个示例:
var i = 0;
while (i < 10) {
i += 2;
}
3.6.5 for-in语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。以下是 for-in 语句的语法:
for (property inexpression) statement
下面是一个示例:
for (var propName inwindow) {
document.write(propName);
}
3.6.6 label 语句
使用 label 语句可以在代码中添加标签,以便将来使用。以下是 label 语句的语法:
label: statement
下面是一个示例:
start: for (var i=0; i< count; i++) {
alert(i);
}
这个例子中定义的 start 标签可以在将来由 break 或continue 语句引用。加标签的语句一般都
要与 for 语句等循环语句配合使用。
3.6.7 break 和continue 语句
break 和continue 语句用于在循环中精确地控制代码的执行。其中, break 语句会立即退出循环,
强制继续执行循环后面的语句。而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶
部继续执行。请看下面的例子:
var num = 0;
for (var i=1; i < 10;i++) {
if (i % 5 == 0) {
break;
}
num++;
}
alert(num); //4
3.6.9 switch语句
switch 语句与 if语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。
ECMAScript 中switch 语句的语法与其他基于 C 的语言非常接近,如下所示:
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
switch (i) {
case 25:
/* 合并两种情形 */
case 35:
alert("25 or35");
break;
case 45:
alert("45");
break;
default:
alert("Other");
}