JavaScript 快速语法参考(一)
一、使用 JavaScript
要开始试验 JavaScript,您应该安装一个支持这种语言的集成开发环境(IDE)。有很多不错的选择,比如 NetBeans、Eclipse、Visual Studio、括号。在本书中,我们将使用 NetBeans,它可以从 Netbeans.org 免费获得。确保你下载了一个包含 HTML 5 支持的包,因为它也包含对 JavaScript 的支持。
或者,您可以使用简单的文本编辑器(如记事本)进行开发,尽管这不如使用 IDE 方便。如果您选择这样做,只需创建一个文件扩展名为.html的空文档,并在您选择的编辑器中打开它。
创建项目
安装 NetBeans 后,继续运行该程序。然后,您需要创建一个项目,该项目将管理 HTML 源文件和网站的其他资源。转到文件➤新项目以显示新项目窗口。从这里,在左侧框架中选择 HTML 5 类别,然后在右侧框架中选择 HTML 5 应用项目。单击 Next 按钮,您可以配置项目的名称和位置。完成后,单击“完成”让向导创建您的项目。
您现在已经创建了一个 HTML 5 项目。在“项目”面板(“➤项目”窗口)中,您可以看到该项目由一个名为“index.html”的文件组成,该文件位于站点的根文件夹中。该文件包含一些基本的 HTML 5 标记,可以进一步简化为如下所示的标记。
<!doctype html>
<html>
<head><title>JavaScript Test</title></head>
<body></body>
</html>
嵌入 JavaScript
将 JavaScript 插入 web 文档有两种方法。第一种是将代码放在一个script元素中。一个文档可以有多个这样的元素,每个元素可以包含任意数量的 JavaScript 语句。
<script></script>
另一种更常见的方法是将代码包含在外部文件中,然后使用script元素的src属性链接到该文件。这样,几个文档就可以使用相同的代码,而不必在每一页上都重复代码。
<script src="mycode.js"></script>
按照惯例,.js扩展名用于包含 JavaScript 代码的文件。要将此名称的新文件添加到 NetBeans 项目中,请右键单击“项目”面板中的站点根文件夹,然后选择“新建➤ JavaScript 文件”。在对话框中,将源文件命名为“mycode.js”,单击 Finish,该文件将被添加到您的项目中并为您打开。
出于实验的目的,您可以使用第一种嵌入方法来内联您的代码。然而,对于真实世界的应用,除了最简单的脚本之外,所有的脚本都应该是外部的。这使得代码更容易阅读和维护,因为它将 JavaScript 代码(页面行为)与 HTML 标记(页面内容)分开。由于浏览器缓存了外部文件,这也提高了网站的性能。
显示文本
在学习一门新的编程语言时,第一个示例 JavaScript 代码将显示一个“Hello World”文本字符串,这是很常见的。这是通过在 web 文档的body元素中添加下面一行来实现的。
<script>
document.write("Hello World");
</script>
这段代码语句使用了属于document对象的write方法。该方法接受文本作为其参数,用双引号分隔。这些概念将在后面的章节中进一步探讨。
JavaScript 中的语句用分号分隔。如果语句后跟换行符,可以省略分号,因为这也被解释为语句分隔符。
document.write("Hello World")
完整的 web 文档现在应该是这样的。
<!doctype html>
<html>
<head><title>JavaScript Test</title></head>
<body>
<script>
document.write("Hello World");
</script>
</body>
</html>
若要查看该网页,请用 web 浏览器打开 HTML 文件。在 NetBeans 中,这是通过单击运行➤运行项目(F6)或单击工具栏上的绿色箭头来完成的。您可以从“运行➤集项目浏览器”中选择您的首选浏览器。在浏览器中查看文档时,一旦页面加载并显示文本字符串,就会执行脚本。
查看源代码
当浏览器打开时,您可以通过按 Ctrl + U 查看组成页面的源代码。该快捷方式适用于所有主流浏览器,包括 Chrome、Firefox 和 Internet Explorer (IE)。源代码窗口显示 HTML 标记,以及未解析的 JavaScript 代码。
以这种方式查看 web 页面的源代码提供了一个向其他 web 开发人员学习的好方法。每当你在一个网页上发现一个有趣的功能——无论它是用 HTML、CSS、JavaScript 还是其他语言制作的——页面源代码通常会揭示它是如何创建的。
浏览器兼容性
JavaScript 通常运行在客户端,在浏览器内部,而不是服务器端。因此,要执行代码,需要客户端在支持 JavaScript 的浏览器中查看文档。
因为 JavaScript 是最流行的客户端脚本语言,所以它几乎可以在今天使用的所有浏览器上工作。但是,客户端可能会选择禁用 JavaScript,因此无法保证客户端代码得到执行。即便如此,今天大多数网站都使用 JavaScript,而且许多网站都依赖它才能正常运行。
HTML 提供了noscript元素,为不支持 JavaScript 或禁用了 JavaScript 的浏览器指定替代内容。
<noscript>
Please enable JavaScript for full functionality of this site.
</noscript>
控制台窗口
大多数浏览器都有一个开发控制台,允许您查看 JavaScript 代码中的信息,以便进行调试。要将信息打印到该控制台,可以使用 console 对象的 log 方法。
<script>
console.log("Hello Console");
</script>
在 Chrome、Firefox 和 Internet Explorer 中,打开控制台的过程是相同的。右键单击页面并选择 Inspect Element。这将打开开发窗口,从中可以找到 Console 选项卡。在 Internet Explorer 中,您需要首先启动开发窗口,然后刷新页面以查看控制台输出。
评论
注释用于向开发人员阐明代码,它们对代码的解析没有影响。JavaScript 具有单行(//)和多行(/**/)注释的标准符号,许多其他语言也使用这种符号。
<script>
// single-line comment
/* multi-line
comment */
</script>
和 HTML 一样,空白字符——比如空格、制表符和注释——在 JavaScript 中通常会被忽略。这让你在如何格式化你的代码上有很大的自由度。您使用的格式是个人喜好的问题。选择一种对你有意义的风格,并力求保持一致。
代码提示
如果您不确定一个特定的对象包含什么,或者一个函数接受什么参数,您可以利用一些 ide 中的代码提示,比如 NetBeans。通过按 Ctrl + Space 打开代码提示窗口,并提供对您能够在当前上下文中使用的任何代码实体的快速访问。这是一个强大的特性,你应该学会好好利用它。
Footnotes 1
二、变量
变量是用于存储数据(如数字或字符串)的容器,因此它们可以在脚本中多次使用。
声明变量
要创建一个变量,可以使用关键字var,后跟一个名称,称为标识符。变量的一个常见命名约定是,除了第一个单词,每个单词最初都要大写。
var myVar;
可以使用等号给变量赋值,这叫做赋值操作符(=)。这被称为分配或初始化变量。
myVar = 10;
声明和赋值可以合并成一条语句。当一个变量被赋值时,它就被定义了。
var myVar = 10;
通过使用逗号操作符(,),有一种在同一个语句中创建多个变量的简便方法。
var myVar = 10, myVar2 = 20, myVar3;
一旦变量被声明,就可以通过引用变量名来使用它。例如,通过将标识符传递给document.write方法,可以将变量的值打印到 web 文档中。
document.write(myVar); // "10"
请记住,变量标识符区分大小写,因此大写和小写字母有不同的含义。JavaScript 中的标识符可以包括字母、美元符号($)、下划线(_)和数字,但不能以数字开头。它们也不能包含空格或特殊字符,并且不能是保留关键字。
var _myVar32; // allowed
var 32Var; // incorrect (starts with number)
var my Var; // incorrect (contains space)
var var@32; // incorrect (contains special character)
var var; // incorrect (reserved keyword)
动态打字
JavaScript 是一种动态类型语言。因此,不需要指定变量的数据类型,任何变量都可以包含任何数据类型。
var myType = "Hi"; // string type
myType = 1.5; // number type
此外,变量的值将根据需要自动转换,这取决于使用它的上下文。
// Number type evaluated as string type
console.log(myType); // "1.5"
由于这些隐式类型转换,知道变量的基础类型并不总是必要的。尽管如此,了解 JavaScript 在后台处理的数据类型还是很有用的。这六种类型如下:数字、布尔、字符串、对象、未定义和空。
数字类型
JavaScript 对于整数和浮点数只有一种类型。整数可以用十进制(基数为 10)、八进制(基数为 8)或十六进制(基数为 16)来表示。整数值上的前导零表示它是八进制的,前导 0x(或 0X)表示十六进制的。十六进制整数可以包括数字 0–9 和字母 A–F,而八进制整数只能包括数字 0–7。下面的整数文字都代表同一个数,在十进制记数法中是 10。
var dec = 10; // decimal notation
var oct = 012; // octal notation
var hex = 0xA; // hexadecimal notation
浮点数可以用十进制或指数(科学)记数法来表示。指数记数法的用法是在十进制指数后加上E(或e)。
var num = 1.23;
var exp = 3e2; // 3*10² = 300
请记住,JavaScript 中的所有数字都在后台存储为双精度浮点数。
布尔类型
bool 类型可以存储布尔值,该值只能为 true 或 false。这些值由关键字true和false指定。
var myBool = true;
布尔值通常与条件语句和循环语句一起使用,这将在后面的章节中讨论。
未定义的类型
JavaScript 有一个名为undefined的值,用来表示没有值。这是声明的变量在没有初始值的情况下得到的值。
var myUndefined;
console.log(myUndefined); // "undefined"
用于存储该值的数据类型也被命名为undefined。这可以通过使用typeof操作符来显示,它检索一个类型的字符串表示。
console.log(typeof myUndefined); // "undefined"
请记住,未定义的变量不同于未声明的变量。任何试图访问未声明变量的行为都将导致抛出ReferenceError异常,从而中止脚本的执行。
console.log(myUndeclared); // throws a ReferenceError
零点类型
类型和值 null 表示没有值的对象。与可能由语言级行为导致的 undefined 相反,null 值总是通过代码设置的。它通常用作指示异常或错误情况的函数返回值。
var myNull = null;
console.log(myNull); // "null"
虽然 null 是一种数据类型,但是typeof操作符会将这种类型作为一个对象来计算。这被认为是语言规范中的一个错误。
console.log(typeof myNull); // "object"
在布尔上下文中,null 和 undefined 都被评估为false。以下示例使用 not 运算符(!)将这些值强制转换为布尔类型。该运算符反转布尔结果,因此被两次用于检索原始值的布尔表示。
console.log(!!null); // "false"
console.log(!!undefined); // "false"
相反,在数值上下文中,null 表现为 0,而 undefined 导致整个表达式计算为 NaN。
console.log(null * 5); // "0"
console.log(undefined * 5); // "NaN"
特殊数值
JavaScript 有三个特殊的数值:Infinity、-Infinity 和 NaN。这些值用于表示在计算过程中发生了一些异常。例如,以下计算得出这三个值。
console.log(1 / 0); // "Infinity"
console.log(-1 / 0); // "-Infinity"
console.log(0 / 0); // "NaN"
值 NaN 是非数字的缩写,表示不可表示的数值。它是数学运算失败时通常使用的返回值。例如,从-1 中取平方根得到 NaN。这个计算可以使用全局 Math 对象的 sqrt 方法来执行。
var myNaN = Math.sqrt(-1);
console.log(myNaN); // "NaN"
console.log(typeof myNaN); // "number"
尝试在数值上下文中计算非数值也会导致 NaN。
console.log("Hi" * 3); // "NaN"
NaN 有一个奇怪的特性,它与任何其他值都不相等,包括另一个 NaN 值。要确定一个值是否为 NaN,可以使用全局 is NaN 函数。
console.log(NaN == NaN); // "false"
console.log(isNaN(myNaN)); // "true"
三、运算符
运算符是一个符号,它使脚本执行特定的数学或逻辑操作。JavaScript 中的运算符可以分为五种类型:算术、赋值、比较、逻辑和按位运算符。
算术运算符
算术运算符包括四种基本算术运算,以及用于获得除法余数的模数运算符(%)。
x = 3 + 2; // 5 - addition
x = 3 - 2; // 1 - subtraction
x = 3 * 2; // 6 - multiplication
x = 3 / 2; // 1.5 - division
x = 3 % 2; // 1 - modulus (division remainder)
赋值运算符
第二组是赋值操作符。最重要的是赋值操作符(=)本身,它给变量赋值。
x = 0; // assignment
组合赋值运算符
赋值运算符和算术运算符的一个常见用途是对变量进行运算,然后将结果保存回同一个变量中。使用组合赋值操作符可以缩短这些操作。
x += 5; // x = x+5;
x -= 5; // x = x-5;
x *= 5; // x = x*5;
x /= 5; // x = x/5;
x %= 5; // x = x%5;
递增和递减运算符
另一种常见的操作是将变量加 1 或减 1。这可以用增量(++)和减量(--)操作符来简化。
x++; // x = x+1;
x--; // x = x-1;
这两者都可以用在变量之前或之后。
x++; // post-increment
x--; // post-decrement
++x; // pre-increment
--x; // pre-decrement
无论使用哪个变量,变量的结果都是相同的。不同的是,后运算符在改变变量之前返回原始值,而前运算符先改变变量,然后返回值。
x = 5; y = x++; // y=5, x=6
x = 5; y = ++x; // y=6, x=6
比较运算符
比较运算符比较两个值,并返回 true 或 false。它们主要用于指定条件,即计算结果为 true 或 false 的表达式。
x = (2 == 3); // false - equal to
x = (2 === 3); // false - identical
x = (2 !== 3); // true - not identical
x = (2 != 3); // true - not equal to
x = (2 > 3); // false - greater than
x = (2 < 3); // true - less than
x = (2 >= 3); // false - greater than or equal to
x = (2 <= 3); // true - less than or equal to
严格相等运算符===和!==用于比较类型和值。这些是必要的,因为常规的等于(==)和不等于(!=)操作符会在比较操作数之前自动执行类型转换。
x = (1 == "1"); // true (same value)
x = (1 === "1"); // false (different types)
当不需要等于运算的类型转换功能时,使用严格比较被认为是一种好的做法。
逻辑运算符
逻辑运算符通常与比较运算符一起使用。如果左右两边都为真,则逻辑 and ( &&)计算为真,如果左右两边都为真,则逻辑 or ( ||)为真。对一个布尔结果取反,有一个逻辑非(!)运算符。请注意,对于“逻辑与”和“逻辑或”,如果结果已经由左侧确定,则不会计算右侧。
x = (true && false); // false - logical and
x = (true || false); // true - logical or
x = !(true); // false - logical not
按位运算符
按位运算符可以处理组成整数的各个位。例如,右移位运算符(>>)将除符号位之外的所有位向右移动,而零填充右移位(>>>)将包括符号位在内的所有位向右移动。这两个运算符对正数的计算是相同的。
x = 5 & 4; // 101 & 100 = 100 (4) - and
x = 5 | 4; // 101 | 100 = 101 (5) - or
x = 5 ^ 4; // 101 ^ 100 = 001 (1) - xor
x = 4 << 1; // 100 << 1 =1000 (8) - left shift
x = 4 >> 1; // 100 >> 1 = 010 (2) - right shift
x = 4 >>>1; // 100 >>> 1 = 010 (2) - zero-fill right shift
x = ∼4; // ∼00000100 = 11111011 (-5) - invert
按位运算符也有组合赋值运算符。
x=5; x &= 4; // 101 & 100 = 100 (4) - and
x=5; x |= 4; // 101 | 100 = 101 (5) - or
x=5; x ^= 4; // 101 ^ 100 = 001 (1) - xor
x=4; x <<= 1; // 100 << 1 =1000 (8) - left shift
x=4; x >>= 1; // 100 >> 1 = 010 (2) - right shift
x=4; x >>>=1; // 100 >>> 1 = 010 (2) - right shift
请记住,JavaScript 数字存储为双精度浮点数。但是,位运算需要对整数进行操作,因此在执行位运算时,数字会临时转换为 32 位有符号整数。
运算符优先级
在 JavaScript 中,表达式通常从左到右计算。但是,当表达式包含多个运算符时,这些运算符的优先级决定了它们的求值顺序。下表显示了优先级顺序,其中优先级最低的运算符将首先被计算。同样的顺序也适用于许多其他语言,比如 PHP 和 Java。
| 在…之前 | 操作员 | 在…之前 | 操作员 | | --- | --- | --- | --- | | one | `() [] . x++ x--` | eight | `&` | | Two | `! ∼ ++x --x` | nine | `^` | | three | `* / %` | Ten | `|` | | four | `+ -` | Eleven | `&&` | | five | `<< >> >>>` | Twelve | `||` | | six | `< <= > >=` | Thirteen | `= op=` | | seven | `== != === !===` | Fourteen | `,` |举个例子,乘法比加法更难绑定,因此将在下面的代码行中首先进行计算。
x = 4 + 3 * 2; // 10
这可以通过将表达式中首先被求值的部分用括号括起来来说明。从表中可以看出,括号在所有运算符中优先级最低。
x = 4 + (3 * 2); // 10
四、数组
数组是用于存储值集合的数据结构。JavaScript 数组可以分为三类:数值型、关联型和多维型。这些数组之间的区别只是概念上的,因为 JavaScript 认为它们都是数组对象。
数字数组
数字数组用数字索引存储数组中的每个元素。可以通过以下方式使用数组构造函数创建空数组。
var a = new Array(); // empty array
要将元素添加到数组中,可以通过将元素的索引放在方括号中来一次引用一个元素。为元素赋值会自动为该元素创建空间,并增加数组的容量。请注意,数组索引从零开始。
a[0] = 1;
a[1] = 2;
a[2] = 3;
数组的初始容量可以通过向数组构造函数传递一个数值参数来指定。这可用于在预先知道数组将容纳的元素数量的情况下提高性能。
var b = new Array(3);
将多个参数或非数字参数传递给数组构造函数会将这些值赋给数组的第一个元素。
var c = new Array(1, 2, 3);
创建数组的另一种方法是将元素值放在方括号中,即所谓的数组文字。这是创建数组的最短且最常用的方法。
var d = [1, 2, 3];
省略数组中的值为创建空数组提供了一种快捷方式。
var e = []; // empty array
通过引用方括号内所需元素的索引来访问数组的内容。
var f = [1, 2, 3];
document.write(f[0] + f[1] + f[2]); // "6"
如果引用的元素不存在,则返回未定义类型的对象。
document.write(f[3]); // "undefined"
注意,就像常规变量一样,数组中的元素可以存储任何数据类型或其组合。
var mixed = [0, 3.14, "string", true];
关联数组
关联数组使用键字符串而不是数字索引来标识元素。要创建一个数组,首先声明一个空数组,然后将值赋给所需的键。
var g = new Array();
g["name"] = "Peter";
g["age"] = 25;
当访问这些元素时,记住键名是很重要的,因为这些数组不能用索引来访问。
document.write(g["name"] + " is " + g["age"]); // "Peter is 25"
JavaScript 中的数组是对象,它们的元素是对象属性。因此,关联数组的元素也可以用点符号来引用。
var h = new Array();
h.name = "Peter";
h.age = 25;
document.write(h.name + " is " + h.age); // "Peter is 25"
数字元素不能以这种方式访问,必须使用括号符号引用。
h[0] = 1;
可以在同一个数组中混合数字元素和关联元素,因为 JavaScript 不会区分它们。事实上,索引是作为键字符串存储在后台的,也可以这样引用。
h["0"] = 1;
多维数组
通过将数组作为元素添加到另一个数组中,可以使数组成为多维的。
var m = [ ["00","01"], ["10","11"] ];
多维数组可以有任意多个维度,但是很少需要两个以上的维度。对于每个额外的维度,添加另一组方括号。
document.write(m[1][1]); // "11"
与许多其他语言不同,JavaScript 中的多维数组不需要所有子数组的长度都相同。随着阵列容量的自动调整,也可以稍后在脚本中更改尺寸。
m[1][2] = "12";
数组对象
array 对象提供对许多用于操作数组的成员的访问。一个这样的成员是 length 属性,它检索或设置数组的当前容量。
var x = [1, 2, 3];
var len = x.length; // 3
x.length = 2; // deletes third element
document.write(x[2]); // "undefined"
IDE 中的代码提示提供了数组对象可用的成员列表。举个例子,pop 方法从数组中移除最后一个元素,push 将一个或多个元素追加到数组的末尾。
var y = [1, 2];
y.push(3); // add element to end of array
y.pop(); // remove last element
五、字符串
字符串由一系列用双引号或单引号分隔的字符组成。使用哪种符号是个人喜好的问题。
var s1 = "Hello";
var s2 = ' World';
有两个运算符可以对字符串进行操作。对于组合字符串,有一个加号(+),在这个上下文中称为连接运算符。它有一个伴随的赋值操作符(+=),将一个字符串附加到一个字符串变量的末尾。
var greeting = s1 + s2; // "Hello World"
s1 += s2; // "Hello World"
要在字符串中换行,必须添加反斜杠。该字符对换行符进行转义,换行符在 JavaScript 中通常表示语句的结束。反斜杠和换行符都从字符串的值中删除。
greeting = "Hello \
World";
转义字符
转义字符用于书写特殊字符,如新行和制表符。这些字符前面总是有一个反斜杠“\”。例如,要在单引号字符串中插入单引号,该标记前面需要有反斜杠。
var s = 'It\'s'; // "It’s"
下表列出了 JavaScript 中可用的转义字符。
| 性格;角色;字母 | 意义 | 性格;角色;字母 | 意义 | | --- | --- | --- | --- | | `\n` | 新行 | `\f` | 换页 | | `\t` | 横表 | `\v` | 垂直标签 | | `\'` | 单引号 | `\"` | 双引号 | | `\b` | 退格键 | `\r` | 回车 | | `\\` | 反斜线符号 | | |除了这些转义字符之外,还有用于引用 Unicode 和 Latin-1 编码字符集的符号。Unicode 字符表示为“\u”,后跟一个 4 位十六进制数。Latin-1 字符可以表示为以“\x”开头的三位八进制数或两位十六进制数。如下图所示,换行符用四种不同的方式表示。
var line = '\n'; // escape code
line = '\012'; // octal Latin-1
line = '\x0A'; // hexadecimal Latin-1
line = '\u000A'; // hexadecimal Unicode
字符串和数字
在同时包含字符串和数值的表达式中,串联运算符会将数字转换为字符串。如果可能的话,其他数值运算符将尝试将字符串转换为数字,否则将计算为 NaN。
"5" + 5; // "55"
"5" - 5; // 0
"a" - 5; // NaN
用字符串表示的数值可以用parseInt函数转换成整数。
parseInt("5") + 5; // 10
类似地,parseFloat可以用来将字符串转换成浮点数。对于这两个函数,只返回字符串中的第一个数字,否则,如果第一个字符不是数字,则该方法返回 NaN。
parseFloat("3.14"); // 3.14
parseFloat("Hi"); // NaN
或者,一元加法运算符(+)可用于执行字符串到数字的转换,方法是将加法符号放在字符串之前。
+"5" + 5; // 10
字符串对象
JavaScript 中的所有字符串都是字符串对象。因此,它们提供了对执行常见字符串操作时有用的属性和方法的快速访问。例如,字符串中的字符数可以使用 length 属性来确定。
var a = "Hello";
var len = a.length; // 5
当您键入点号来访问 string 对象的成员时,IDE 会提供代码提示,为您提供可用成员的完整列表。例如,toLowerCase方法将字符串转换成小写字母。返回结果字符串,而不更改原始字符串。
var lower = a.toLowerCase(); // "hello"
JavaScript 将任何一段文本解释为 string 对象的一个实例。因此,可以直接在字符串常量上调用方法,就像在字符串变量上一样。
var upper = "abc".toUpperCase(); // "ABC";
六、条件语句
条件语句用于根据不同的条件执行不同的代码块。
如果语句
只有当括号内的表达式被求值为 true 时,if语句才会执行。在 JavaScript 中,这不必是布尔表达式。它可以是任何表达式,在这种情况下,零、null、NaN、空字符串和未定义的变量被计算为 false,而所有其他值为 true。
if (x < 1) {
document.write("x < 1");
}
为了测试其他条件,if语句可以被任意数量的else if子句扩展。只有当前面的条件为假时,才会测试每个附加条件。
else if (x > 1) {
document.write("x > 1");
}
对于处理所有其他情况,可以在末尾有一个else子句,如果所有先前的条件都为假,则执行该子句。
else {
document.write("x == 1");
}
如果只需要有条件地执行一条语句,可以省去花括号。但是,始终包含它们被认为是一种好的做法,因为它们可以提高代码的可读性。
if (x < 1)
document.write("x < 1");
else if (x > 1)
document.write("x > 1");
else
document.write("x == 1");
交换语句
switch语句检查表达式和一系列 case 标签之间的相等性,然后将执行传递给匹配的 case。表达式可以是任何类型,并且将使用严格比较(===)来匹配案例标签。开关可以包含任意数量的 case 子句,并且可以以处理所有其他情况的默认标签结束。
switch (x) {
case 0: document.write("x is 0"); break;
case 1: document.write("x is 1"); break;
default: document.write("x is not 0 or 1"); break;
}
注意,每个 case 标签后的语句以关键字break结束,以跳过开关的其余部分。如果省略了 break,执行将一直进行到下一个案例,如果需要以相同的方式评估几个案例,这将非常有用。
三元运算符
除了if和switch语句之外,还有三元运算符(?:,它为单个if else语句提供了快捷方式。这个运算符有三个表达式。如果第一个为真,则计算并返回第二个表达式;如果为假,则计算并返回第三个。
// Ternary operator expression
y = (x === 1) ? 1 : 2;
在 JavaScript 中,该运算符也可以用作独立的代码语句,而不仅仅是表达式。
// Ternary operator statement
(x === 1) ? y = 1 : y = 2;
编程术语“表达式”指的是计算出一个值的代码,而语句是以分号或右花括号结束的代码段。
七、循环
循环语句用于多次执行一个代码块。JavaScript 有四种循环:while、do-while、for和for-in。与条件if语句一样,如果代码块中只有一条语句,可以省略这些循环的花括号。
While 循环
只有当条件为真时,while循环才会遍历代码块,并且只要条件保持为真,循环就会继续。
var i = 0;
while (i < 10) {
document.write(i++); // 0-9
}
这里的循环将打印出数字 0 到 9。请记住,条件只在每次迭代开始时检查。
Do-While 循环
除了检查代码块之后的条件之外,do-while循环的工作方式与while循环相同。因此,它将始终至少在代码块中运行一次。注意,这个循环以分号结束。
var j = 0;
do {
document.write(j++); // 0-9
} while (j < 10);
For 循环
for循环在代码块中运行特定的次数。它使用三个参数。第一个初始化一个计数器,并且总是在循环之前执行一次。第二个参数保存循环的条件,并在每次迭代之前进行检查。第三个参数包含计数器的增量,在每次迭代结束时执行。
for (var k = 0; k < 10; k++) {
document.write(k); // 0-9
}
这个循环有几种变化,因为任何一个参数都可以省略。例如,如果省略第一个和第三个参数,它的行为方式与while循环相同。
var k;
for (; k < 10;) {
document.write(k++); // 0-9
}
第一个和第三个参数也可以使用逗号运算符(,)拆分成几个语句。
for (var k = 0, m = 0; k < 10; k++, m--) {
document.write(k+m); // 000... (10x)
}
属性获取数组中元素的数量。与for循环一起,它可以用来遍历一个数组。
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
document.write(a[i]); // "123"
}
如果不需要跟踪迭代,for-in循环提供了一个更短的遍历数组的语法。
For-in 循环
for-in循环提供了一种简单的方法来遍历数组中的元素或对象中的属性。在每次迭代中,将下一个属性的键或索引赋给变量,循环继续迭代,直到遍历完对象的所有成员。
var colors = ["red","green","blue"];
for (i in colors) {
document.write(colors[i] + " "); // "red green blue"
}
中断并继续
有两个跳转语句可以在循环内部使用:break和continue。break关键字结束循环结构,而continue跳过当前迭代的剩余部分,并在下一次迭代的开始处继续。
for (var i = 0; i < 10; i++)
{
if (i == 2) continue; // start next iteration
else if (i == 5) break; // end loop
document.write(i); // "0134"
}
要中断当前循环之上的循环,必须首先标记外部循环,方法是在外部循环前添加一个名称,后跟一个冒号。有了这个标签,它现在可以用作break语句的参数,告诉它从哪个循环中退出。这也适用于continue关键字,以便跳到指定循环的下一次迭代。
myloop:
for (var i = 0; i < 10; i++)
{
var j = 0;
while (++j < 10)
{
break myloop; // end for loop
}
}
八、函数
函数是可重用的代码块,只有在被调用时才会执行。它们允许开发人员将他们的脚本分成更小的部分,更容易理解和重用。
定义函数
要创建一个函数,可以使用 function 关键字,后跟一个名称、一组括号和一个代码块。函数的命名惯例与变量相同——使用一个描述性的名称,除了第一个单词以外,每个单词都要大写。
function myFunc()
{
document.write("Hello World");
}
这个函数只是向 web 文档显示一个文本字符串。函数代码块可以包含任何 JavaScript 代码,包括其他函数定义。
调用函数
一旦定义了一个函数,就可以在文档的任何地方调用它,只需键入它的名字,后面加上一组括号。函数名区分大小写,所以字母的大小写需要一致。
myFunc(); // "Hello World"
即使函数定义稍后出现在脚本中,也可以调用函数。这是因为 JavaScript 中的声明是在代码执行之前处理的。
foo(); // ok
function foo() {}
函数参数
函数名后面的括号用于向函数传递参数。为此,必须首先将相应的参数添加到函数的参数列表中。这些参数可以作为常规变量在函数中使用。
function sum(a, b) {
var sum = a + b;
console.log(sum);
}
一个函数可以被定义为接受任意数量的参数。调用该函数时,参数以逗号分隔列表的形式提供。在此示例中,该函数接受两个数值参数,并显示它们的总和。
sum(2, 3); // "5"
像变量一样,函数参数也没有在声明中指定类型。因此,不会自动执行类型检查,函数参数也不限于任何特定的数据类型。
可变参数列表
允许调用一个函数,其参数个数与定义的参数个数不同。如果调用函数时使用的参数较少,那么剩余的参数将被设置为未定义。
function say(message) {
console.log(message);
}
say(); // "undefined"
当调用一个函数的参数比它的定义中的多时,多余的参数将没有名字。可以通过类似数组的 arguments 对象引用这些额外的参数,该对象包含传递给函数的所有参数。
function say() {
console.log(arguments[0]);
}
say("Hello"); // "Hello"
arguments 对象可用于创建 varadic 函数,这些函数能够处理不同数量的参数。举个例子,可以将任意数量的参数传递给下面的函数。该函数遍历参数,并将它们组合成一个字符串,输出到控制台。
function combine() {
var result = "";
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
console.log(result);
}
combine(1, 2, 3); // "123";
返回语句
Return 是一个跳转语句,它使函数退出,并将指定的值返回到调用该函数的地方。举例来说,下面的函数返回其两个参数的和。这个函数又可以作为参数传递给另一个函数,在那里它将计算出结果数。
function getSum(a, b) {
return a + b; // exit function and return value
}
console.log( getSum(1, 2) ); // "3"
return 语句也可以用来在到达 end 块之前退出函数,而不返回任何特定的值。没有返回值的函数将隐式返回 undefined。
function foo() {
return; // exit function
}
console.log( foo() ); // "undefined"
就像变量和参数一样,返回值不进行类型检查。函数需要被适当地文档化,以便函数的用户知道他们的输入和输出应该是什么。
参数传递
参数通过值传递给函数。对于基本类型,这意味着只有值的副本被传递给函数。因此,以任何方式更改参数都不会影响原始变量。
function set(y) {
y = 1;
}
var x = 0;
set(x); // copy of value passed
console.log(x); // "0"
当对象类型用作参数时,传递的是对该对象的引用。这允许函数对原始对象的属性进行更改。
function addFruit(basket) {
basket[0] = "Apple";
}
var fruits = [];
addFruit(fruits); // copy of reference passed
console.log( fruits[0] ); // "Apple"
将新对象赋给参数不会影响函数外部的原始对象。这是因为赋值改变了参数的值,而不是对象的一个属性值。
function makeFruit(basket) {
basket = [ "Apple" ];
}
var fruits = [];
makeFruit(fruits);
console.log( fruits[0] ); // "undefined"
函数表达式
JavaScript 中的函数是对象,特别是函数对象。因此,它们可以被赋给变量并传递给其他函数,就像任何其他对象一样。当一个函数以这种方式使用时,它被称为函数表达式,而不是函数声明。可以使用正常的赋值语法(包括分号)将函数表达式赋给变量。
var say = function foo(message)
{
console.log("Hello " + message);
};
当调用一个函数对象时,引用的是变量名而不是函数名。
say("World"); // "Hello World"
可以在函数内部使用函数名来引用它自己,但是函数名在其他情况下是不必要的。因此,该名称通常被省略。然后,函数表达式被称为匿名函数。
var say = function(message)
{
console.log("Hello " + message);
};
函数表达式通常被用作回调函数,或者被传递给其他函数,或者从其他函数返回。它们允许非常简洁地编写代码,因为函数可以内联,而不必在其他地方定义它。为了说明这一点,下面的例子使用了window.setTimeout函数,它采用另一个函数和一个数字作为参数。该数字指定在调用函数参数之前等待的毫秒数。
// Call anonymous function after one second
window.setTimeout(function() { console.log("Hello") }, 1000);
范围和寿命
变量的作用域指的是可以使用该变量的代码区域。JavaScript 中的变量既可以全局声明,也可以局部声明。全局变量是在任何函数之外声明的,可以从文档中的任何地方访问。另一方面,局部变量是在函数内部声明的,并且只能在该函数内部访问。
var globalVar = 0; // global variable
function foo() {
var localVar = 0; // local variable
}
局部变量的生存期是有限的。全局变量将在脚本运行期间保持分配状态,而局部变量将在其函数执行完毕后被销毁。
console.log(globalVar); // "0"
foo();
console.log(localVar); // throws a ReferenceError
当作用域中的两个变量同名时,就会出现名称冲突。更多的内部作用域具有优先权,因此最里面的作用域具有最高的优先权,而最外面的作用域具有最低的优先权。
var a = "global";
function foo() {
var a = "local"; // overshaddows global variable
console.log(a);
}
foo(); // "local"
console.log(a); // "global"
与许多其他语言不同,JavaScript 中的代码块没有自己的作用域。因此,在控制结构代码块(如循环或条件语句)中定义的变量在代码块结束时不会被销毁。
if(true) {
var x = 10; // global variable
}
console.log(x); // "10"
还有一种创建变量的替代方法,即在不使用 var 关键字的情况下为未声明的变量赋值。这将隐式地将变量声明为全局变量,即使它是在函数中声明的。
function foo() {
a = 5; // global variable
}
foo();
console.log(a); // "5"
以这种方式错误地引入或覆盖全局变量是常见的错误来源。因此,建议始终使用 var 关键字显式声明变量。
var a = 10;
foo(); // replaces value of global variable
console.log(a); // "5"
像函数声明一样,显式变量声明也在脚本执行之前进行处理。因此,变量可以在声明之前在代码中引用。
console.log(a); // "undefined"
var a = "defined";
console.log(a); // "defined"
对于隐式声明的变量来说,这种行为是不同的,因为在为变量赋值的代码运行之前,这些变量是不存在的。
console.log(b); // throws a ReferenceError
b = "defined"; // never executes
九、对象
对象是被称为属性的命名值的集合。属性可以是保存对象状态的变量,也可以是定义对象功能的函数。对象的吸引力在于它们在提供功能的同时隐藏了它们的内部工作。你需要知道的只是一个对象能为你做什么,而不是它是如何做的。
对象属性
可以使用 new 指令以下面的显式方式创建一个空对象。
var box = new Object();
使用点符号或数组符号将对象的属性赋给时,会自动创建这些属性。
box.x = 2;
box["y"] = 3;
同样,属性可以用这两种方式之一引用。
console.log(box.x); // "2"
console.log(box["y"]); // "3"
可以使用 delete 指令从对象中删除属性。这不能用常规变量来完成,只能用对象属性来完成。
box.z = 1; // add property
delete box.z; // delete property
要检查对象是否包含属性,可以使用 in 运算符。然后,属性名被指定为字符串。
console.log("x" in box); // "true"
console.log("z" in box); // "false"
对象方法
函数可以以属性的形式添加到对象中。这种功能被称为方法。当引用函数声明时,括号被省略。
box.getArea = myArea;
function myArea() { return this.x * this.y; }
这里使用的this关键字是对当前拥有该函数的对象的引用。如果在对象上下文之外调用函数,关键字将改为引用window对象。为了防止这种脱离上下文的调用,最好使用函数表达式内联函数。这也防止了函数名不必要地弄乱了全局名称空间。
box.getArea = function() { return this.x * this.y; };
一旦绑定到对象,就可以以熟悉的方式调用该方法。this关键字在这里指的是 box 对象,它具有前面定义的属性x和y。
console.log( box.getArea() ); // "6"
对象文字
创建对象的一种更简单的方法是使用对象文字,它由一组花括号分隔。创建空对象时,括号是空的。
var box = {};
使用对象文字的优点是,可以在创建对象时设置属性,方法是将它们包含在花括号中。属性的每个名称-值对由冒号分隔,每个属性依次由逗号分隔。
var box = {
x: 2,
y: 3,
getArea: function() { return this.x * this.y; }
};
如果只需要一个对象实例,那么对象文字就很有用。如果需要多个实例,可以使用函数构造函数。
构造函数
可以从构造函数中创建对象。通过允许从一组定义中创建一个对象的多个实例,它们提供了其他语言中的类所提供的功能。按照惯例,打算用作对象构造函数的函数以大写字母开头,以提醒它们的用途。
function Box(x, y) {
this.x = x;
this.y = y;
this.getArea = function() { return this.x * this.y; };
}
为了从这个构造函数中实例化一个或多个对象,用new指令调用这个函数。就像任何其他函数一样,构造函数可以接受参数,如下例所示。
var b1 = new Box(1, 2);
var b2 = new Box(3, 4);
每个对象实例都包含自己的一组属性,这些属性可以保存与其他实例不同的值。
console.log(b1.x); // "1"
console.log(b2.x); // "3"
先前定义的构造函数包括在另一个函数中声明的函数。这种嵌套函数可以访问其父函数中定义的变量,因为它形成了一个包含外部函数范围的所谓闭包。这种对象创建模式的一个优点是它提供了信息隐藏。例如,下面的示例有一个方法,它使用一个局部变量来记录该方法被调用的次数。这个特性类似于基于类的语言中的私有属性,因为局部变量只在构造函数中可见。
function Counter(x, y) {
var count = 0;
this.printCount = function() { console.log(count++); };
}
var c = new Counter();
c.printCount(); // "0";
c.printCount(); // "1";
这种对象创建模式的一个小缺点是每个实例都有自己的printCount方法,这会增加每个对象消耗的内存。避免这种情况的另一种模式是利用继承将方法添加到对象的原型中。
遗产
一个对象可以从另一个对象继承属性。这为对象重用其他对象的代码提供了一种方式。专门化的对象通常称为子对象,更一般的对象称为父对象。
在 JavaScript 中,继承是通过原型继承模型实现的。在这个模型中,每个对象都有一个内部属性,作为到另一个对象的链接,称为它的原型。考虑下面的对象。
var myObj = new Object();
这个对象从内置的Object构造函数继承属性。当请求一个对象不直接包含的属性时,JavaScript 将自动搜索继承链,直到找到请求的属性或到达链的末端。
// Add a property to myObj
myObj.x = 5;
// Call a method from Object
myObj.hasOwnProperty("x"); // true
链中的最后一个链接总是内置的Object构造函数,它的原型链接又是 null,这标志着链的结束。可以使用__proto__属性检索对对象原型链接的引用。这不要与构造函数的 prototype 属性混淆,它是函数的文字对象表示,当创建该函数的新实例时,它被分配给__proto__。
myObj.__proto__ === Object.prototype; // true
myObj.__proto__.__proto__ === null; // true
通过扩展原型链来实现继承。这可以在构造函数中完成,方法是将prototype属性设置为要继承的对象。
function Parent() { this.a = "Parent"; }
function Child() { }
Child.prototype = new Parent();
var child = new Child();
console.log(child.a); // "Parent"
类型检查
您可以通过比较对象的__proto__链接和构造函数的prototype属性来手动确认对象的类型。
child.__proto__ === Child.prototype; // true
JavaScript 还提供了instanceof操作符。该操作符在原型链中导航,如果左侧对象指向原型链中右侧的构造函数,则返回 true。
child instanceof Child; // true
child instanceof Parent; // true
child instanceof Object; // true
对象创建
执行继承的另一种方式是通过Object.create方法。此方法提供了一种更简单的实现继承的方法,它允许一个对象直接从另一个对象继承,而无需使用额外的构造函数。
var p = new Parent();
var c1 = Object.create(p); // inherit from p
方法的第二个可选参数允许您使用特殊的表示法初始化对象的属性。
var c2 = Object.create(p, { name: { value: "Child 2" } } );
console.log(c2.name); // "Child 2"
对象的prototype属性可以用来动态地向原型添加属性。这将导致链接到该原型的所有对象都继承新的属性。
Parent.prototype.x = "new property";
console.log(c2.x); // "new property"
如本章所示,JavaScript 在创建和使用对象时提供了很大的灵活性。选择使用哪种方法往往归结为个人喜好的问题。
十、文档对象模型
文档对象模型或 DOM 是一个编程接口,它描述了 web 文档的所有元素以及它们之间的相互关系。通过这个接口,JavaScript 代码可以与 web 文档进行交互。
DOM 节点
在 DOM 模型中,web 文档的内容以由节点组成的树状结构表示。通过了解这些节点是如何组织的,就有可能动态地更改文档的任何部分。考虑下面的 HTML 标记。
<p>My paragraph</p>
这个标记在 DOM 树中创建了两个节点:一个元素节点和一个文本节点。元素节点可以有子节点,这里的文本节点是段落元素的子节点。相反,段落节点是文本节点的父节点。转到下一个示例,有四个节点。
<div id="mydiv">
<!-- My comment -->
</div>
div 元素节点包含三个节点:两个文本节点和一个注释节点。额外的文本节点来自本例中注释前后的空格(空格、制表符和换行符)。查看网页时,这四个节点都是不可见的。
每个元素可以有一个可选的 id 属性,在本例中应用于 div 元素。因为这个属性必须是惟一的,所以它提供了一种在 DOM 树中选择特定节点的便捷方式。注意,DOM 规范也认为属性是节点;但是,在导航 DOM 树时,属性节点被视为元素节点的属性。
选择节点
document 对象表示 web 文档,并提供了几种访问 DOM 树和检索节点引用的方法。最常见的方法是使用getElementById方法。此方法返回对具有指定唯一 id 的元素的引用。使用这个方法,可以检索前面示例中的 div 元素节点。
var mydiv = document.getElementById("mydiv");
也可以用getElementsByTagName方法通过标签名选择元素。此方法检索该类型的所有元素的类似数组的集合。
var mydivs = document.getElementsByTagName("div");
另一种不太常见的选择元素的方式是通过class属性。由于多个元素可以共享同一个类名,因此该方法也返回一个集合。除 IE < 9 之外,所有现代浏览器都支持它。
var myclasses = document.getElementsByClassName("myclass");
getElementById方法只能从文档对象中调用,但是另外两个方法可以从 DOM 树中的特定节点调用,以便只搜索该节点的子节点。例如,下面的代码检索 body 元素下的所有元素节点。
var myelements = document.body.getElementsByTagName("*");
除了 body 节点,document 对象还具有检索对html和head节点的引用的属性。
var htmlnode = document.documentElement;
var headnode = document.head;
var bodynode = document.body;
也可以使用querySelector方法定位元素节点。该方法返回匹配指定 CSS 选择器的第一个元素,在本例中是 class 属性设置为myclass的第一个元素。
var mynode = document.querySelector(".myclass");
类似地,querySelectorAll方法检索匹配给定 CSS 查询的所有元素节点的集合。除了 IE < 9,所有现代浏览器都支持这两种查询方式。
var mynodes = document.querySelectorAll(".myclass");
如果找不到元素,则返回 null。这种行为在所有 DOM 方法中都是一致的。
遍历 DOM 树
一旦选择了一个节点,就有许多属性允许相对于该节点遍历 DOM 树。下面的列表可以用来说明。
<ul id="mylist">
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
对这个无序列表节点的引用是通过它的 id 来检索的。
var mylist = document.getElementById("mylist");
此节点下的元素节点可通过 children 集合获得。第一个列表项也可以使用firstElementChild属性来检索。
var first = mylist.children[0];
first = mylist.firstElementChild;
同样,可以通过 children 集合或lastElementChild属性访问最后一个节点。
var third = mylist.children[2];
third = mylist.lastElementChild;
可以使用previousElementSibling和nextElementSibling属性将 DOM 树左右导航到相邻节点。在这种情况下,兄弟指的是共享同一个父节点的节点。
var second = first.nextElementSibling;
second = third.previousElementSibling;
这四个属性——firstElementChild、lastElementChild、nextElementSibling和previousElementSibling——除了 IE < 9,所有现代浏览器都支持。对于完整的浏览器支持,可以使用以下属性来代替:firstChild、lastChild、nextSibling和previousSibling。请记住,这些属性也考虑文本和注释节点,而不仅仅是元素节点。
parentNode属性引用父节点。与其他属性一起,它们允许在所有四个方向上遍历 DOM 树:上、下、左、右。
mylist = first.parentNode;
子集合只包含元素节点。一个例外是在 9 之前的 IE 版本中,这个集合也包括注释节点。如果需要所有节点类型,则使用childNodes集合。该集合包含所有子节点,包括元素、文本和注释节点。再一次,IE < 9 的行为与其他浏览器不同,在childNodes集合中没有包含纯空白的文本节点。
mylist.childNodes[0]; // whitespace text node
mylist.childNodes[1]; // li node
mylist.childNodes[2]; // whitespace text node
对于元素节点,nodeName和tagName属性都包含大写字母的标签名称。它们可以用来测试元素节点的标记名。
if (mydiv.nodeName == "DIV")
console.log(mydiv.tagName); // "DIV"
虽然tagName属性专门用于元素节点,但是nodeName属性对于任何节点类型都是有用的。例如,注释节点的计算结果为“#comment”,文本节点的计算结果为“#text”。
mylist.childNodes[0].nodeName; // #text
mylist.childNodes[1].nodeName; // LI
mylist.childNodes[2].nodeName; // #text
创建节点
DOM 中的节点可以动态地添加、删除或更改。举例来说,一个新的列表项将被添加到先前的列表中。第一步是分别使用createElement和createTextNode方法创建元素和文本节点。
var myitem = document.createElement('li');
var mytext = document.createTextNode("New list item");
然后,使用 list item 节点上的appendChild方法,将文本节点添加到元素节点。
myitem.appendChild(mytext);
接下来,使用相同的方法将列表项节点添加到无序列表中。这将导致新列表项出现在页面上。
mylist.appendChild(myitem);
appendChild方法添加它的节点参数作为调用它的元素节点的最后一个子节点。为了在其他地方插入节点,使用了insertBefore方法。此方法采用第二个节点参数,在该参数之前插入新节点。
mylist.insertBefore(myitem, mylist.children[0]);
一个节点不能同时出现在两个地方,因此该操作会删除之前添加到列表末尾的节点,而将其放在列表的开头。要添加另一个类似的节点,可以首先使用cloneNode方法复制元素。此方法采用一个布尔参数,该参数指定元素的后代节点是否也将被复制。
var newitem = myitem.cloneNode(false);
接下来,这个节点被添加到列表的末尾,但是因为它是在没有后代的情况下克隆的,所以它没有文本节点,所以在文档中显示为空列表元素。
mylist.appendChild(newitem);
如前所示,可以使用createTextNode和appendChild方法创建和链接文本节点。一个更短的替代方法是修改innerHTML属性,它代表元素的 HTML 内容。
newitem.innerHTML = "<b>Another</b> new list item";
该属性允许自动创建元素和文本节点。另一个有用的类似属性是textContent。此属性表示去除了任何 HTML 标记的元素内容。
newitem.textContent; // "Another new list item"
newitem.innerHTML; // "<b>Another</b> new list item"
删除节点
可以使用removeChild方法删除一个节点。该方法返回对已移除节点的引用:在本例中,是列表中的最后一个子节点。请记住,在 JavaScript 中捕捉返回值是可选的。
var removedNode = mylist.removeChild(mylist.lastElementChild);
另一种删除节点的方法是用不同的节点替换它。这是通过replaceChild方法完成的,该方法也返回被替换的节点。下面的代码用以前移除的节点替换列表的第一个子节点。
mylist.replaceChild(removedNode, mylist.firstElementChild);
属性节点
属性节点可以通过其包含元素来访问,而不是作为该元素的子节点。为了便于说明,这里有一个带有id属性的段落,以便于选择。
<p id="myid">My paragraph</p>
它的元素节点以熟悉的方式选择。
var mypara = document.getElementById("myid");
setAttribute方法向被引用的元素添加一个属性,或者替换它的值,如果它已经存在的话。它有两个参数:属性和值。
mypara.setAttribute("class","myclass");
为了检索属性的值,使用了getAttribute方法。在检索属性之前,这里使用hasAttribute方法执行检查以确保它存在。
if (mypara.hasAttribute("class"))
console.log(mypara.getAttribute("class")); // "myclass"
属性自动与元素节点的属性同步。这为设置和获取属性提供了一种不太冗长的方式。属性和它们对应的属性共享相同的名称,除了 class 属性的属性名为className。这是因为 class 是 JavaScript 中的保留关键字。
console.log(mypara.id); // "myid"
console.log(mypara.className); // "myclass"
使用style属性可以动态改变元素的 CSS 属性。一种方法是直接修改属性。这将覆盖以前通过该属性设置的任何内联样式。
mypara.setAttribute("style", "background-color: yellow;");
相反,要给元素添加新的样式,可以改变style对象的属性。这些属性与其对应的 CSS 属性具有相同的名称,只是删除了所有连字符,并且第一个单词后面的每个单词都大写。
mypara.style.backgroundColor = "yellow";
要恢复样式更改,您只需通过将该属性设置为空字符串来清除它。
mypara.style.backgroundColor = "";
十一、事件
事件是在用户、网页和浏览器之间的交互中发生的事件。事件处理使脚本能够检测这些事件并做出反应,从而使网页变得具有交互性。
事件处理
处理事件有三个步骤。首先,您需要定位将接收事件的元素。事件总是发生在 DOM 树中元素节点的上下文中。在此示例中,当单击以下超链接时,将发生该事件。
<a href="http://www.google.com
下一步是创建事件处理程序,这是事件发生时将执行的代码。该事件处理程序通常由一个函数组成,在这种情况下,该函数为用户显示一个带有消息的警告框。
function myEventHandler() {
alert("Event triggered");
}
最后,最后一步是为要处理的特定事件注册事件处理程序。这里选择了 link 元素节点,事件处理程序以下面的方式注册到它的onclick事件中。
var mylink = document.getElementById("mylink");
mylink.onclick = myEventHandler;
当用户点击这个链接时,调用事件处理程序,弹出警告框。一旦盒子被关闭,默认的事件处理程序就会接管,链接就会像平常一样被跟踪。若要移除事件处理程序,请将属性设置为 null。
mylink.onclick = null;
这被称为事件注册的传统模型。另一种更短的注册方法是内联模型,它使用事件属性将事件处理程序直接附加到产生事件的 HTML 元素上。
<a href="http://www.google.com
请注意,事件处理程序的括号包含在内联模型中,而不是传统模型中。使用这个模型的一个更简单的方法是内联这个函数。
<a href="http://www.google.com
内联事件处理程序可以包含多个语句。在下面的示例中,return false 语句被添加到事件处理程序中。这将阻止默认的浏览器操作发生,在这种情况下,这意味着该链接将不再被跟踪。
<a href="http://www.google.com
所有现代浏览器都支持传统模型和内联模型。传统模型通常更可取,因为它允许通过代码添加、更改和删除事件处理程序,并且它完全将 JavaScript 与 HTML 分开。
W3C 在 DOM level 2 规范中标准化了第三种注册事件的模型。在这个 W3C 模型中,使用addEventListener方法添加了一个事件处理程序。这个方法有三个参数:事件类型、事件处理程序和一个布尔值,我们将在后面看到。
mylink.addEventListener("click", myEventHandler, false);
注意,在 W3C 模型中,事件的前缀“on”被省略了,所以“onclick”就变成了“click”要删除一个事件处理程序,removeEventListener与同样的三个参数一起使用。
mylink.removeEventListener("click", myEventHandler, false);
W3C 模型的主要优点是可以为同一个事件和同一个元素节点注册多个事件处理程序。一个缺点是 IE<9 不支持,这使得它比其他两种方法的跨浏览器兼容性差。
事件对象
当事件被触发时,浏览器向事件处理程序传递一个参数,将事件表示为一个对象。可以通过向事件处理程序添加参数来访问该对象。
function myEventHandler(e) { }
这个对象是访问事件信息的 W3C 方法。IE<9 没有传递事件对象参数,而是有一个全局的window.event对象,代表最后触发的事件。为了跨浏览器的兼容性,可以将下面一行添加到处理程序的开头,以确保在所有浏览器中都能检索到正确的事件对象。
function myEventHandler(e) {
if (!e) var e = window.event;
}
事件对象通过其属性提供关于事件的附加信息。不同的事件有不同的属性,但是所有的事件对象都有type属性。该属性保存一个标识事件的字符串,例如 onclick 事件的“click”。
console.log(e.type); // "click"
大多数事件也有一个目标,它是对触发事件的元素节点的引用。在前面的例子中,这指的是锚元素。
console.log(e.target.tagName); // "A"
在 IE<9 上,事件对象有一个srcElement属性,而不是target属性。这里可以看到跨浏览器兼容的目标检索方式。
var target = e.target || e.srcElement;
事件传播
大多数 DOM 事件都有事件传播,这意味着内部元素触发的事件也会触发外部元素。举例来说,下面是一个嵌套在div元素中的段落元素。
<div id="outer">Outer element
<p id="inner">Inner element</div>
</div>
下面的代码使用传统模型为这两个元素注册 click 事件。
var inner = document.getElementById("inner");
inner.onclick = function() { alert("Inner"); }
var outer = document.getElementById("outer");
outer.onclick = function() { alert("Outer"); }
在内部元素上触发一个事件后,它继续触发以嵌套顺序附加到父元素的任何事件处理程序。因此,单击内部元素将首先显示“内部”消息,然后是“外部”消息。这种默认的事件顺序称为冒泡。
相反的事件顺序称为捕获。IE<9 只有冒泡顺序,但其他所有现代浏览器都是先捕获再冒泡的方式处理事件处理程序。为了注册捕获阶段的事件处理程序,addEventListener的最后一个参数被设置为 true。在下面的示例中,当单击段落元素时,数字将按顺序打印。
outer.addEventListener("click", function() { console.log("1"); }, true);
inner.addEventListener("click", function() { console.log("2"); }, true);
inner.addEventListener("click", function() { console.log("3"); }, false);
outer.addEventListener("click", function() { console.log("4"); }, false);
当事件处理程序被触发时,它有机会通过调用事件对象上的stopPropagation方法来防止事件进一步传播。在 IE < 9 中,事件的cancelBubble属性需要改为设置为 true。下面是一种跨浏览器兼容的阻止事件传播的方法。
function cancelEvent(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
DOM 事件
现代浏览器支持很多 DOM 事件。以下是您可能会遇到的最常见事件的列表。
| 事件名称 | 描述 | | --- | --- | | `onClick` | 当用户单击一个元素时触发。可以应用于任何可见的元素。 | | `onLoad` | 当页面完成加载时在窗口上触发。需要外部对象的元素,比如`<script>document.getElementById("para").style.color = "red";</script>
<p id="para">My paragraph</p>
onload事件通过将初始化代码移动到注册到该事件的事件处理程序中,提供了解决这个问题的方法。有了这些代码,一旦文档被加载,段落文本就会被染成红色。
function colorText() {
document.getElementById("para").style.color = "red";
}
window.addEventListener("load", colorText, false);
注意,onload事件是为 window 对象注册的,它代表浏览器窗口。这是文档对象以及所有全局代码和变量所属的 DOM 层次结构中的最顶层对象。
再举一个例子,onMouseOver和onMouseOut事件通常用于创建图像翻转效果。下面的示例代码通过在用户将鼠标指针移到图像上时切换显示的图像来说明这一点。
<img src="pic1.png" id="rollover"
onmouseover = "document.getElementById('rollover').src='pic2.png'"
onmouseout = "document.getElementById('rollover').src='pic1.png'">
十二、Cookie
cookie 是从网站发送并存储在用户本地计算机上的一段数据。它们提供了一个持久的存储空间,允许网站在用户在页面之间移动或返回网站时记住他们。
创建 Cookies
Cookies 是通过给document.cookie对象分配一个名称-值对来创建的。它们仅限于存储字符串值。
document.cookie = "cookie1=mycookie";
相同的对象用于检索 cookie 字符串。
console.log(document.cookie); // "cookie1=mycookie"
要修改一个 cookie,你只需要给它分配一个新值。这将覆盖以前的 cookie。
document.cookie = "cookie1=first";
当另一个名称-值对被分配给cookie对象时,另一个 cookie 被自动创建。
document.cookie = "cookie2=second";
引用cookie对象检索一个字符串,该字符串包含所有 cookies 的分号分隔列表。
console.log("document.cookie"); // "cookie1=first;cookie2=second"
通过使用 string 对象的split方法,可以从 cookie 字符串中提取每个名称-值对。方法返回包含拆分值的数组,这些值根据作为参数指定的分隔符进行拆分。
var dataList = document.cookie.split(";");
console.log(dataList[0]); // "cookie1=first"
console.log(dataList[1]); // "cookie2=second"
再次使用split可以将名称与值分开。
var pair = dataList[0].split("=");
console.log(pair[0] + " is " + pair[1]); // "cookie1 is first"
编码
cookie 的名称-值对不能包含空格、逗号或分号。为了确保不包含这些字符,可以使用全局escape方法对字符串进行 URL 编码。
document.cookie = "cookie1=" + escape("Foo Bar");
在 URL 编码格式中,空格被替换为%20。
console.log(document.cookie); // "cookie1=Foo%20Bar"
当读回 cookie 字符串时,unescape方法用于撤销编码。
console.log(unescape(document.cookie)); // "cookie1=Foo Bar"
到期日期
除了名称-值对之外,cookie 字符串还可以包含其他信息,比如 cookie 应该何时过期。默认情况下,cookie 的生命周期限于当前浏览器会话。当用户关闭窗口时,该会话结束,之后浏览器删除 cookie。使用内置的Date对象及其toGMTString方法,以下面的方式设置到期日期。通过将这个毫秒数加到由getTime方法返回的值上,这个 cookie 被设置为一小时后过期。
var mycookie = "cookie1=first";
var date = new Date();
date.setTime(date.getTime() + (60*60*1000));
document.cookie = mycookie + ";expires=" + date.toGMTString();
这个截止日期也提供了删除 cookies 的方法。这是通过使用旧的到期日期重新创建同一个 cookie 来实现的,在本例中,到期日期是过去的一天。
var name = "cookie1";
var date = new Date();
date.setDate(date.getDate() - 1);
document.cookie = name + '=;expires=' + date.toGMTString();
请注意,只需要 cookie 的名称来标识要覆盖哪个 cookie。因此,值部分为空。
路径和域
为了访问 cookie,文档必须位于相同的域中,并且具有由 cookie 指定的相同路径。默认情况下,该路径是包含创建 cookie 的文档的文件夹的位置。以下示例说明了如何使 cookie 对给定域中的所有路径可见。
var mycookie = "cookie1=first";
var path = "path=/";
document.cookie = mycookie + ";" + path;
域默认为文档所在位置的主机部分,比如www.example.com。为了使 cookie 对该域的其他子域可用,可以省略www部分,如下例所示。
var domain = "domain=.example.com";
document.cookie = mycookie + ";" + domain + ";" + path;
域、路径和过期选项可以按任意顺序出现。请记住,一旦设置了这些可选参数,就不可能从 cookie 中检索它们。
十三、错误处理
错误是开发人员需要修复或处理的代码中的错误。例如,由于缺少括号,下面的代码行将触发语法错误。
console.log("Hi";
遇到错误时,浏览器会暂停脚本的执行。大多数浏览器在出错时不会通知用户。相反,可以在浏览器的开发人员控制台中找到有关错误的信息。这些信息包括文件名、行号和错误描述,如下所示。
SyntaxError: missing ) after argument list
虽然语法错误很容易发现和解决,但其他错误可能更难发现,因为它们可能只在特定情况下出现。此外,由于开发人员无法控制的原因,可能会出现一些错误。例如,如果某个函数由外部文件定义,并且该文件无法加载,则该函数可能不可用。这会触发参考误差。
// Uncaught ReferenceError: missingFunc is not defined
missingFunc();
防止脚本执行暂停的一种方法是在调用函数之前检查它是否存在,这可以通过以下方式实现。
if (typeof missingFunc === "function") {
missingFunc(); // safe to use function
}
假设这个错误发生在一个函数中,并且这个函数不能继续运行。然后,该函数需要通知它的调用者它失败了。这通常是通过从函数返回 null 或错误代码来完成的。
function foo() {
if (typeof missingFunc === "function") {
missingFunc(); // safe to use function
}
else { return null; }
}
这通常是函数处理错误或异常的最佳方式,因为调用方通常拥有决定如何响应异常所必需的上下文。然而,有时可能有多个堆叠的函数调用,使得这种方法更加麻烦。随着每个函数返回到它的调用者,异常的上下文逐渐丢失,最后一个返回 null 值的函数不知道哪里出错了。为了解决这个问题,JavaScript 提供了try - catch语句。
试着接住
try - catch语句由一个包含可能导致异常的代码的try块和一个用于处理异常的catch子句组成。如果try块成功执行,程序将在try - catch语句后继续运行,但如果出现异常,执行将被传递到catch块。
try {
missingFunc();
}
catch(e) {}
有了这个语句,异常处理代码只在知道如何处理错误时才是必需的。错误和异常处理程序之间的函数不需要关心异常。
捕捉块
catch子句定义了一个异常对象。此对象可用于获取有关异常的更多信息,例如来自消息属性的异常描述。
catch(e) {
// "missingFunc is not defined"
console.log(e.message);
}
请注意,JavaScript 没有提供选择性捕获异常的直接方法。所有潜在的异常都需要在同一个catch块中处理。还要记住,语法错误是无法捕捉的。
最后
作为try - catch语句的最后一个子句,可以添加一个finally块。该块用于清理try块中分配的资源,无论是否有异常,都将一直执行。
try {
missingFunc();
}
catch(e) { }
finally {
console.log("Always executed");
}
抛出异常
用户定义的异常可以用throw语句生成。当到达该语句时,函数停止执行,异常沿调用方堆栈向上传播,直到被try - catch语句捕获,或者被浏览器处理。
function bar()
{
if (typeof missingFunc === "function") {
missingFunc();
}
else { throw new Error("missingFunc(): Function missing"); }
}
关键字throw后面可以跟函数想要发出的信号。在这个例子中,Error构造函数用于创建一个异常值。这种标准类型通常用于创建用户定义的异常。它将错误描述作为字符串参数。该参数可通过 error 对象的 message 属性获得。
try {
bar();
}
catch(e) {
// "missingFunc(): Function missing"
console.error(e.message);
}
Error对象也包含一个name属性。对于用Error构造函数创建的对象,该属性的值为“错误”。
console.error(e.name); // "Error"
为了更容易识别异常,最好抛出自定义错误对象。当try子句中的代码可以抛出多种类型的异常时,这尤其有用。在下面的代码示例中,引发了一个带有自定义名称和消息属性的对象文本。可以在catch子句中检查name属性,以确定应该如何处理异常。
throw {
name: 'CustomError',
message: 'Error description'
}
十四、AJAX
异步 JavaScript 和 XML(或 Ajax)是一种在后台与服务器交换数据的方法,无需刷新整个页面。这允许基于用户事件来更新页面的元素,而不中断用户在页面上所做的事情,从而实现了高度响应的 web 应用的开发。
交换数据
Ajax 请求是使用XMLHttpRequest对象发出的。发出请求的第一步是创建这个对象的一个实例。
var myRequest = new XMLHttpRequest();
为了处理服务器的响应,将一个事件处理程序附加到该对象的 onload 事件。这里用responseText属性检索响应,并显示在控制台中。
myRequest.addEventListener('load', myHandler);
function myHandler() {
console.log(this.responseText);
}
使用对象的open方法初始化请求,调用该方法有三个参数:检索方法、URL 和一个布尔值。检索方法通常是“GET”或“POST”获取数据时通常使用“GET”方法,发送数据时通常使用“POST”方法。URL 位置是服务器数据可用的位置。这可以是任何文本文件,如 HTML 或 XML 文档,也可以是生成文本响应的服务器端脚本。最后一个参数指定请求是否将被异步处理,这通常是首选,或者是否应该让send方法等到收到响应。
var url = "mytext.txt";
myRequest.open("GET", url, true);
请求准备好了,现在可以使用send方法发送了。如果信息要提交给服务器上的脚本,这个数据被指定为send方法的参数。
myRequest.send();
为了跟踪和测试这个 Ajax 请求,可以将一个名为mytext.txt的文本文件和一些示例文本放在 web 文档所在的文件夹中。请记住,Ajax 受 JavaScript 同源策略的约束,这意味着指定的位置必须位于加载脚本的同一服务器上。
服务器响应
当收到服务器响应时,应该检查 HTTP 状态代码以确保请求成功。通过status属性可以获得状态代码,一个成功的请求由数字 200 表示。其他状态代码表示响应有问题,如 404,表示找不到文件。
function myHandler() {
if (this.status === 200)
console.log(this.responseText);
}
这里使用responseText属性检索响应,该属性将响应作为文本字符串返回。如果响应是 XML 数据,那么可以使用responseXML属性将其作为 XML 对象进行检索。可以使用标准的 DOM 方法遍历这样的对象。
Ajax 事件
Ajax 请求的状态可以从XMLHttpRequest对象的readyState属性中检索。该状态用 0 到 4 的数字表示,当状态达到 4 时,响应被完全接收。可以在onreadystatechange属性上附加一个函数来响应每个状态变化事件。这可用于向用户更新请求的进度。
myRequest.onreadystatechange = stateObserver;
function stateObserver() {
switch(this.readyState) {
case 0: console.log("Request not sent"); break;
case 1: console.log("Request sent"); break;
case 2: console.log("Response header received"); break;
case 3: console.log("Downloading data"); break;
case 4: console.log("Response data received"); break;
}
}
十五、框架
jQuery 框架是一个广泛使用的 JavaScript 库,旨在简化 DOM 操作并消除浏览器不兼容性。
包括 jQuery
要包含 jQuery,您可以在 web 文档的<head>部分放置以下引用。这将从 Google 的托管库服务中加载最新版本的 jQuery,在撰写本文时是版本 1.11.3。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js
如果您喜欢脚本文件的本地副本,可以从jquery.com获得。然而,从内容交付网络(CDN)链接到 jQuery,比如上面的链接,提供了性能优势。托管 jQuery 的 CDN 服务器分布在全球各地,减少了延迟,已经从同一来源获得缓存副本的访问者不必再次下载。
使用 jQuery
当包含 jQuery 脚本时,它会向全局名称空间添加一个名为jQuery的对象。该对象提供对 jQuery 框架的实用方法的访问。其中许多方法旨在简化常用的 JavaScript 功能,例如isArray、isPlainObject和isFunction方法,它们提供了检查传递的参数是否属于这些类型之一的简单方法。
jQuery.isArray( [1, 2] ); // true
jQuery.isPlainObject( {} ); // true
jQuery.isFunction( function() {} ); // true
jQuery对象通常通过它的美元符号($)别名来引用,因为它输入起来更短,简洁是 jQuery 的一个关键特性。因此,可以用更短的语法调用 jQuery 方法。这里使用了type方法,它以字符串的形式返回参数的类型。
$.type(null); // "null"
$.type( [1, 2] ); // "array"
在这种情况下,type方法比typeof操作符更具体。
typeof( null ); // "object"
typeof( [1, 2] ); // "object"
元素选择
对 DOM 元素的访问和操作需要首先选择目标元素,jQuery 极大地简化了这个过程。假设一个 web 文档包含下面我们想要选择的元素。
<p id="myid">My paragraph</p>
jQuery对象可以用作一个函数,它接受一个参数并返回一个 jQuery 集合。如果参数是 CSS 选择器,则对象返回一个集合,其中包含与该选择器匹配的任何 DOM 元素。这为选择段落提供了一种方便的方法。
var selection = $("#myid");
与更冗长的 DOM 方法相比,DOM 方法通过 id 选择元素。
var domNode = document.getElementById("myid");
这些选择方法之间的一个重要区别是 DOM 方法返回对 DOM 元素的引用,而 jQuery 方法返回一个 jQuery 集合。该集合充当底层 DOM 元素的包装器,为在这些元素上执行操作提供了更易于使用的方法。如果希望使用原生 DOM 方法,可以使用方括号符号或 get 方法将 jQuery 集合展开到原始 DOM 节点。
domNode = selection[0];
domNode = selection.get(0);
相反,通过将 DOM 元素传递给jQuery对象,可以将其包装到 jQuery 集合中。
var selection = $(domNode);
jQuery 集合就像一个包含零个或多个 DOM 元素的数组。如果没有元素与提供的选择器匹配,或者没有提供选择器,则返回的集合为空。
var empty = $();
可以使用length属性检索所选元素的数量。在这种情况下,集合的长度为零。
console.log(empty.length); // "0"
如果选择器匹配多个元素,则返回的集合将包含所有匹配的元素。考虑以下div元素。
<div class="myclass" id="div1">First</div>
<div class="myclass" id="div2">Second</div>
通过类名选择这些元素将返回包含两个元素的集合。
var divs = $(".myclass");
console.log(divs.length); // "2"
可以使用add方法将新的选择添加到收藏中。
var div1 = $("#div1");
divs = div1.add("#div2");
同样,not方法从集合中取出一个或多个匹配的元素。
var div2 = divs.not("#div1");
有趣的是,jQuery 方法调用是可链接的,因为它们都返回 jQuery 集合。下面一行将所有的div元素添加到一个空集合中,然后移除div2元素,只留下div1元素。
var div1 = $().add("div").not("#div2");
集合遍历
一旦选定,就可以通过集合对象的方法来操作集合。为了说明,将使用下面包含三个列表项的列表。
<ul id="mylist">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
以下查询匹配这些列表项,并将它们作为集合返回。
var items = $("#mylist li");
这个items集合现在包含三个 jQuery 元素。为了从这个集合中提取单个元素,可以使用eq方法,将元素的索引作为参数。
var first = items.eq(0);
var second = items.eq(1);
提供负参数将检索从集合末尾开始计数的元素。
var third = items.eq(-1);
快捷方法可用于检索集合的第一个或最后一个元素。
first = items.first();
third = items.last();
或者,在选择元素时,这些方法名可以用作后缀。
first = $("#mylist li:first");
second = $("#mylist li:eq(1)");
third = $("#mylist li:last");
还有其他几个与索引相关的选择器或过滤器,它们允许非常精确的选择查询。例如,odd、even、lt(小于)和gt(大于)过滤器。
var first_third = $("#mylist li:even");
second = $("#mylist li:odd");
first = $("#mylist li:lt(1)");
third = $("#mylist li:gt(1)");
DOM 遍历
可以相对于集合中的元素遍历 DOM 树。使用前面的列表示例,这里的第二个列表项被选为起点。
var second = $("#mylist li:eq(1)");
使用next和prev方法,可以从这个元素将 DOM 横向导航到兄弟元素。
var third = second.next();
var first = second.prev();
为了在层次结构中向上移动,提供了parent方法。在这种情况下,无序列表元素被选中。
var list = second.parent();
children方法可用于在树中向下移动。它在这里将所有三个列表项作为集合返回。
var items = list.children();
要选择一个元素的所有兄弟元素,可以使用siblings方法。请记住,原始元素不包含在同级元素中。
var first_third = second.siblings();
如果集合包含多个元素,大多数集合方法都会影响集合中的所有元素。例如,对包含第一个和第二个列表元素的集合调用 next 将返回包含第二个和最后一个列表元素的集合。
var first_second = $("#mylist li:lt(2)");
var second_third = first_second.next();
修改属性
提供了attr方法来访问和修改所选元素的属性。为了说明,考虑下面的 HTML。
<p id="myid" class="myclass">First paragraph</p>
<p class="myclass">Second paragraph</p>
当用属性名调用时,attr方法检索该属性的当前值。如果集合包含多个元素,就像在这种情况下,则只检索集合中第一个元素的属性值。
$(".myclass").attr("id"); // "myid"
若要添加或更改属性的值,该属性的名称及其新值将被传递给方法。因为这个集合包含两个元素,所以它们都将被赋予新的属性值。
$(".myclass").attr("title", "mytitle");
通过将一个对象传递给由属性-值对组成的方法,可以同时设置多个属性。
$(".myclass").attr({
lang: "en",
dir: "rtl"
});
可以使用removeAttr方法删除属性。如果应用于多个元素的选择,每个元素都将删除该属性。
$(".myclass").removeAttr("lang");
css方法允许通过元素的style属性检索或更改 CSS 样式。在下面的例子中,第一段是红色的。
$("#myid").css("color", "red");
向该方法传递单个参数将从style属性中检索该样式属性的值,如果该属性尚未设置,则为 undefined。
$("#myid").css("color"); // "rgb(255, 0, 0)"
要改变元素的样式,最好应用在外部样式表中定义这些样式的类,而不是修改style属性。addClass和removeClass方法通过允许在class属性中添加或删除类,提供了一种方便的方法。这两种方法都采用单个参数,该参数是一个字符串,指定要添加或删除的一个或多个类。
$("#myid").addClass("class1 class2");
$("#myid").removeClass("class2");
$("#myid").attr("class"); // "myclass class1"
hasClass方法确定任何匹配的元素是否被分配给指定的类。给定前面的 HTML,下面的代码返回 true。
$("#myid").hasClass("myclass"); // true
创建元素
除了操作现有的 DOM 节点,jQuery对象还可以用来创建新元素,方法是将 HTML 标记以字符串参数的形式传递给它。该字符串必须由一个顶级 HTML 元素组成,但该元素可以包含任意数量的子节点。
var myDiv = $("<div>My container</div>");
jQuery 提供了两种方法来修改元素的内容:text和html。html方法将元素的内容更改为给定的字符串。
myDiv = myDiv.html("<b>Hello HTML</b>");
调用不带参数的方法将检索该元素的 HTML 内容。如果集合包含多个元素,则只返回第一个元素的标记。
// "<b>Hello HTML</b>"
console.log(myDiv.html());
相比之下,调用不带参数的text方法会检索去除了任何 HTML 标签的元素内容。
// "Hello HTML"
console.log(myDiv.text());
同样,当给定一个参数时,text方法将转义字符串中的任何 HTML,并将其作为纯文本内容插入元素中。
myDiv.text("<b>Hello text</b>");
// <b>Hello text</b>
console.log(myDiv.html());
请记住,新创建的元素不会自动包含在 DOM 中。它们只作为各自 jQuery 集合的一部分存在。
移动元素
可以使用一种 jQuery 插入方法在 DOM 树中移动或插入元素。举例来说,下面两个元素。
<div id="content">My content</div>
<div id="container"></div>
append方法将一个元素作为其参数,并将其添加到 jQuery 集合的每个元素的末尾。在本例中,内容元素被选中并作为最后一个子元素移动到容器元素中。
$("#container").append($("#content"));
要将元素作为第一个子元素插入,请使用prepend方法。
$("#container").prepend($("<div>First child</div>"));
after方法将元素作为兄弟元素直接放在集合之前。
$("#container").after($("<div>After</div>"));
before方法以同样的方式工作,但是将它的参数作为兄弟添加到集合之前。
$("#container").before($("<div>Before</div>"));
在执行这些代码行时,HTML 被转换成如下所示的标记。
<div>Before</div>
<div id="container">
<div>First child</div>
<div id="content">My content</div>
</div>
<div>After</div>
就绪方法
在大多数情况下,运行脚本的适当时间是 web 文档已经加载并且完整的 DOM 层次结构可用的时候。为此,jQuery 提供了ready方法,该方法将在该事件发生时执行的函数作为其参数。
function myHandler(e) { console.log("DOM ready"); }
$(document).ready(myHandler);
文档选择器可以省略,因为ready方法只能在文档的上下文中调用。
$(myHandler); // alternative syntax
请记住,jQuery ready 事件不同于 DOM load 事件,DOM load 事件只有在所有外部资源(如图像)完全加载后才会触发。
事件处理
jQuery 的on方法简化了事件处理。此方法将事件的事件处理程序附加到选定的元素。举个例子,考虑下面的列表。
<ul id="mylist">
<li>Item 1</li>
<li>Item 2</li>
</ul>
这些列表项的单击事件可以用下面的代码在一个步骤中注册。
$("#mylist li").on("click", myListHandler);
下面的事件处理程序使用 this 选择器来检索事件发生的元素的引用,因此单击第一个列表项将在控制台中显示“Item 1”。
function myListHandler(e) {
console.log( $(this).text() );
}
有些事件有速记法,比如click、change、keypress、blur、focus。这为前面注册事件处理程序的示例提供了一种替代语法。
$("#mylist li").click(myListHandler);
通过 jQuery 添加事件处理程序提供了与注册事件的 W3C 方法相同的好处,允许为同一个元素和事件附加多个事件处理程序。与 W3C 方法不同,它还具有与 IE6-8 跨浏览器兼容的额外优势,在成功注册事件和传递正确的事件对象方面都是如此。
AJAX
jQuery 极大地简化了使用 Ajax 的步骤,整个初始化、发送和接收服务器请求的过程只需要一个方法。在第十四章中使用 jQuery 显示来自服务器的文本文件内容的例子。
$.ajax({
url: "mytext.txt",
type: "GET",
async: "true",
success: function(data) {
console.log(data);
}
});
如果 Ajax 请求成功,这里指定的success方法将被调用。只有url属性必须为ajax方法设置。其他属性是可选的,可以不指定使用它们的默认值。在这种情况下,type和async属性都使用默认值,因此不需要指定它们。
$.ajax({
url: "mytext.txt",
success: function(data) {
console.log(data);
}
});
通过使用get方法,有另一种更短的方法来完成同样的任务。该方法使用 HTTP GET 请求来请求服务器数据。如果请求成功,将执行两个参数 URL 和回调函数。
$.get("mytext.txt", function(data, status) {
console.log(data);
});
类似于get方法,还有一个post方法使用 HTTP POST 请求发送服务器请求。该方法包括一个额外的参数,该参数包含要发送到服务器的数据。
$.post("myscript.php", {
name: "Alex",
age: "25"
}, function(data, status) {
console.log(data);
});
发送 Ajax 请求的 jQuery 方法自动增加了对 IE<7 的向后兼容性。具体来说,ajax方法考虑到 IE5-6 没有XMLHttpRequest对象,而是使用 ActiveX 对象。