JavaScript高级程序设计 - 读书笔记
JavaScript高级程序设计 - 读书笔记
JavaScript高级程序设计 - 读书笔记 by JerryChan
第2章 在HTML中嵌入JavaScript
第3章 基本概念
3.1.4 严格模式
3.3 变量
3.4 数据类型
3.4.3 Null类型
3.4.4 Boolean类型
3.4.5 Number类型
3.4.6 String类型
3.4.7 Object类型
3.5 操作符
3.6 语句
3.7 函数
第4章 变量、作用域和内存问题
4.1 基本类型和引用类型的值
4.2 执行环境和作用域
4.3 垃圾收集
第五章 引用类型
5.2 Array类型
5.3 Date类型
5.4 RegExp类型
5.5 Function类型
5.6 基本包装类型
第六章 面向对象的程序设计
第7章 函数表达式
7.1 递归实现
7.2 闭包
7.3 块级作用域
7.4 私有变量
第8章 BOM
第9章 客户端检测
第10章 DOM
第11章 DOM扩展
11.1 Selectors API
11.1.1 querySelector()
11.1.2 querySelectorAll()
11.2 元素遍历
11.3 HTML5
11.3.1 与类相关的扩充
11.3.2 焦点管理
11.3.3 HTMLDocument的变化
11.4 专有扩展
11.4.2 children属性
11.4.3 contains()方法
第12章 DOM2和DOM3
第13章 事件
13.1 事件流
13.2 事件处理程序
13.2.2 DOM0级事件处理程序
13.2.3 DOM2级事件处理程序
13.2.5 跨浏览器的事件处理程序
13.3 事件对象
13.4 事件类型
13.5 内存和性能
13.6 模拟事件
第16章 HTML5脚本编程
16.1 跨文档消息传递
16.2 原生拖放
16.2.1 拖放事件
16.2.2 自定义放置目标
16.2.3 dataTransfer对象
16.2.4 dropEffect与effectAllowed
16.2.5 可拖动
16.3 媒体元素
16.4 历史状态管理
第17章 错误处理与调试
17.2 错误处理
17.2.1 try-catch语句
17.2.2 抛出错误
第18章 JavaScript与XML
第20章 JSON
20.1 语法
20.2 解析与序列化
20.2.3 解析选项
第21章 AJAX和Comet
21.1.1 XHR的用法
21.1.3 GET请求
21.1.4 POST请求
21.2 XMLHTTPRequest 2级
21.2.1 FormData
21.2.3 overrideMimeType() 方法
21.3 进度事件
21.3.1 load事件
21.3.2 progress事件
21.4 跨域资源共享
21.5 其他跨域技术
JavaScript高级程序设计 - 读书笔记 by JerryChan
第2章 在HTML中嵌入JavaScript
2.5 小结
把JavaScript插入到HTML页面中要使用
元素。使用这个元素可以把JavaScript嵌入到HTML页面中,让脚本与标记混合在一起;也可以包含外部的JavaScript文件。而我们需要注意的地方有:
在包含外部JavaScript文件时,必须将src属性设置为指向相应文件 的 URL。而这个文件既可以是与包含它的页面位于同一个服务器上的文件,也可以是其他任何域中的文件。
所有
元素都会按照它们在页面中出现的先后顺序依次被解析。在不使用defer和async属性的情况下,只有在解析完前面
元素中的代码之后,才会开始解析后面
元素中的代码。
由于浏览器会先解析完不使用defer属性的
元素中的代码,然后再解析后面的内容,所以一般应该把
元素放在页面最后,即主要内容后面,
标签前面。
使用defer属性可以让脚本在文档完全呈现之后再执行。延迟脚本总是按照指定它们的顺序执行。
使用async属性可以表示当前脚本不必等待其他脚本,也不必阻塞文档呈现。不能保证异步脚本按照它们在页面中出现的顺序执行。
另外,使用
元素可以指定在不支持脚本的浏览器中显示的替代内容。但在启用了脚本的情况下,浏览器不会显示
元素中的任何内容。
第3章 基本概念
3.1.4 严格模式
ECMAScript 5引入了严格模式(strictmode)的概念。严格模式是为JavaScript定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 3中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:
"use strict";
在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:
functiondoSomething(){
"usestrict";
//函数体
}
严格模式是一个编译指示,目的是为了兼容 ECMAScript 3语法。
3.3 变量
虽然省略var
操作符可以定义全局变量,但这也不是我们推荐的做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了var
操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致抛出ReferenceError
错误。
省略var
可声明全局变量,但不推荐使用。
3.4 数据类型
ECMAScript中有5种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String。
使用操作符typeof可以检测变量的数据类型。
3.4.3 Null类型
实际上,undefined值是派生自null值的,因此ECMA-262规定对它们的相等性测试要返回true:
alert(null == undefined); //true
无论在什么情况下都没有必要把一个变量的值显式地设置为undefined,可是同样的规则对null却不适用。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,而且也有助于进一步区分null和undefined。
3.4.4 Boolean类型
可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean值。至于返回的这个值是true还是false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则。
Boolean
true
false
String
任何非空字符串
""(空字符串)
Number
任何非零数字值(包括无穷大)
0和NaN(参见本章后面有关NaN的内容)
Object
任何对象
bull
Undefined
n/a
undefined
n/a(或N/A),是notapplicable的缩写,意思是“不适用”。
3.4.5 Number类型
八进制字面量在严格模式下是无效的,会导致支持的JavaScript引擎抛出错误。在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。
永远不要测试某个特定的浮点数值。这是使用基于IEEE754数值的浮点计算的通病。
由于内存的限制,ECMAScript并不能保存世界上所有的数值。ECMAScript能够表示的最小数值保存在Number.MIN_VALUE中——在大多数浏览器中,这个值是5e-324;能够表示的最大数值保存在Number.MAX_VALUE中——在大多数浏览器中,这个值是1.7976931348623157e+308。如果某次计算的结果得到了一个超出JavaScript数值范围的值,那么这个数值将被自动转换成Infinity值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转换成Infinity(正无穷)。
NaN,即非数值(NotaNumber)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。NaN有两个特点。首先,任何涉及NaN的操作(例如NaN/10)都会返回NaN,这个特点在多步计算中有可能导致问题。其次,NaN与任何值都不相等,包括NaN本身。例如,下面的代码会返回false:
alert(NaN == NaN); //false
针对NaN的这两个特点,ECMAScript定义了isNaN()函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值”。例如:
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10是一个数值)
alert(isNaN("10")); //false(可以被转换成数值10)
alert(isNaN("blue"));//true(不能转换成数值)
alert(isNaN(true)); //false(可以被转换成数值1)
使用parseInt()进行转换时,应当在第二个参数指定基数。
3.4.6 String类型
在不知道要转换的值是不是是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
如果值是null,则返回"null";
如果值是undefined,则返回"undefined"。
3.4.7 Object类型
3.5 操作符
左移/右移(<>>):以符号位填充。
无符号左移/右移(<<>>>):以0填充。负数会变成无符号数(出现大整数的错误结果)。
在涉及Infinity、-Infinity、NaN等数值的运算时要倍加小心。
字符串比较时,比较的是字符编码(如B>a
为真,"23" < "3"
为真),最好避免隐式转换。
尽量使用全等===
和不全等!==
操作符,避免隐式转换带来的问题。
3.6 语句
with语句:大量使用with语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with语句。
for-in语句:用于枚举对象的所有属性。
3.7 函数
对参数的理解
ECMAScript函数的参数与大多数其他语言中函数的参数有所不同。ECMAScript函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不会有什么怨言。之所以会这样,原因是ECMAScript中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过arguments
对象来访问这个参数数组,从而获取传递给函数的每一个参数。
其实,arguments
对象只是与数组类似(它并不是Array的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是arguments[0]
,第二个元素是arguments[1]
,以此类推),使用length属性来确定传递进来多少个参数。ECMAScript不支持重载,但利用这一属性,可以模仿方法的重载。
function sayHi() {
alert("Hello" + arguments[0] + "," + arguments[1]);
}
这个重写后的函数中不包含命名的参数。虽然没有使用name和message标识符,但函数的功能依旧。这个事实说明了ECMAScript函数的一个重要特点:命名的参数只提供便利,但不是必需的。
第4章 变量、作用域和内存问题
4.1 基本类型和引用类型的值
第3章讨论了5种基本数据类型:Undefined、Null、Boolean、Number和String 。这5种基本数据类型是按值访问 的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象(Object) 。与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问 的。
思考:
function setName( obj) {
obj. name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName( person);
alert( person. name); //"Nicholas"
//函数结束后,函数内创建的引用被销毁,只有对原引用的修改生效。
4.2 执行环境和作用域
JavaScript没有块级作用域,函数是最小的执行环境。执行环境是其中声明变量的作用域。
4.3 垃圾收集
大多数现代浏览器在变量离开执行环境时就会进行内存清除工作。
频繁调用垃圾收集会产生性能问题(不建议使用window. CollectGarbage()
或类似的函数)。
优化内存占用的方式:
一旦数据不再有用,将其值设置为null
,即解引用(主要针对全局变量)。
局部变量离开执行环境时会被自动解引用。
第五章 引用类型
5.2 Array类型
length属性并非只读,可以通过length修改长度。
检测对象是否Array:
如果使用instanceof操作符,在包含多个全局执行环境的情况下可能出错。
应使用isArray()。
Array提供了栈、队列、排序、连接、查找等方法。重点掌握:
splice()
:基于当前数组的一个或多个项创建数组。
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors. slice( 1);
var colors3 = colors. slice( 1, 4);
alert( colors2); //green, blue, yellow, purple
alert( colors3); //green, blue, yellow
splice()
:对数组指定项执行删除/插入/替换操作。
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
// start:开始位置 deleteCount:删除项的数量 item1...:插入的项
迭代方法:every(),forEach(),filter(),map(),some()
传入的参数为迭代函数,该函数接受item, index, array
三个量。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var everyResult = numbers.every(function(item, index, array){
return (item > 2);
});
alert( everyResult);
缩小方法:reduce(), reduceRight()
与上类似,常用于累加等操作。
5.3 Date类型
创建Date对象:
无参数:取当前日期和时间
使用 Date.parse("datestring")
作为参数:注意非法日期返回值
使用 Date.UTC(year,month,day,hrs,min,sec,msec)
作为参数(月份从0开始)
继承的方法:
toString()
和toLocaleString()
有格式差别。
valueOf()
返回日期的毫秒表示。
5.4 RegExp类型
三种标志:
g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
两种构建形式:
使用字面量构建:var pattern1 = /[bc]at/i;
使用RegExp构造函数构建:var pattern2 = new RegExp("[bc]at", "i");
使用RegExp构造函数,转义字符需要双重转义。
5.5 Function类型
没有重载
内部属性:arguments
和this
arguments
有callee
属性,该指针指向拥有该arguments
的函数。
callee
属性用于函数递归可以降低函数的耦合程度。
this
引用的是函数据以执行的环境对象。
函数有caller
属性,该指针指向调用该当前函数的引用。
函数属性:length
和prototype
length
定义了函数希望接受的命名参数的个数。
prototype
与函数继承的实现有关,之后详述。
函数方法:apply()
和call()
apply()
相当于设置函数体内this对象的值。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1,num2){
return sum.apply( this, arguments); //传入arguments对象
}
function callSum2(num1, num2){
return sum.apply( this, [num1, num2]); // 传入数组
}
alert( callSum1( 10, 10)); //20
alert( callSum2( 10, 10)); //20
call()
与apply()
作用相同,但传递参数的方式不同。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum. call( this, num1, num2);
}
alert( callSum( 10, 10)); //20
apply()
和call()
真正强大的地方是能够扩充函数赖以运行的作用域。使用call()
或apply()
来扩充作用域的最大最大好处,就是对象不需要与方法有任何耦合关系。
ECMAScript 5还定义了一个方法:bind()
。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()
函数的值。
window. color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this. color);
}
var objectSayColor = sayColor. bind(o);
objectSayColor(); //blue
5.6 基本包装类型
为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型:Boolean、Number和String。每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。如String的substring()
方法等。
实际处理过程如下:
实践中永远不使用Boolean对象
第六章 面向对象的程序设计
6.2 创建对象
ECMAScript 通过原型来实现面向对象的程序设计,而非类或接口。
不推荐在产品化的程序中修改原生对象的原型。如果因某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支持该方法的实现中运行代码时,就可能会导致命名冲突。而且,这样做也可能会意外地重写原生方法。
原型模式的问题:对于包含引用类型属性的类而言,修改其中一个对象的引用类型属性会导致其他共享同一原型的对象也发生变化。 例:person1
,person2
共享同一原型,原型包含Array类型的friend
属性,执行person1.friend.push_back("Jacob"); alert(person2.friend); // Jacob
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。例:
function Person( name, age, job){
this. name = name;
this. age = age;
this. job = job;
this. friends = ["Shelby", "Court"];
}
Person. prototype = {
constructor : Person,
sayName : function(){
alert( this. name);
}
}
其他创建方式:动态原型模式、寄生构造函数模式、稳妥构造函数模式(需要时查询)。
6.3 继承
JavaScript使用原型链实现继承,其本质是重写原型对象,代之以一个新类型的示例。
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
可以通过两种方式来确定原型和实例之间的关系。第一种方式是使用instanceof
操作符,第二种方式是使用isPrototypeOf()
方法。
原型链的第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
继承方式 :最常用的是组合继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。例:
//组合使用构造函数模式和原型模式
function SuperType(name){
this. name = name;
this. colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert( this. name); };
function SubType(name, age){
//继承属性
SuperType.call(this, name);
this. age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert( this. age);
};
var instance1 = new SubType(" Nicholas", 29);
instance1. colors. push("black");
alert( instance1. colors); //"red, blue, green, black"
instance1. sayName(); //"Nicholas";
instance1. sayAge(); //29
var instance2 = new SubType(" Greg", 27);
alert( instance2. colors); //"red, blue, green"
instance2. sayName(); //"Greg";
instance2. sayAge(); //27
第7章 函数表达式
关于函数声明:函数声明提升 :是在执行代码之前会先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面。
7.1 递归实现
使用arguments.callee
在严格模式下不能通过脚本访问arguments.callee,则可以使用匿名函数。如:
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num- 1);
}
});
7.2 闭包
闭包 是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
在匿名函数从上一级函数中被返回后,它的作用域链被初始化为包含上一级函数的活动对象和全局变量对象。这样,匿名函数就可以访问在上一级函数中定义的所有变量。
更为重要的是,上一级函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当上一级函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,上级函数的活动对象才会被销毁。
过度使用闭包会导致内存占用过多,请只在绝对必要时考虑使用闭包。
作用域链的副作用 :闭包只能取得包含函数中任何变量的最后一个值。例如:
function createFunctions(){
var result = new Array();
for (var i= 0; i < 10; i++){
result[ i] = function(){
return i;
};
}
return result;
}
// 执行 var a = createFunctions(); a[0](); // 10
原因 :result中每一项都是匿名函数,这些匿名函数返回了createFunctions()
中声明的i。createF1unctions()
运行结束时,i
的值为10,由于因此调用所有的匿名函数返回的值都是10。另外,这些匿名函数不销毁,createFunctions()
中的变量也不会释放。
解决办法 :创建另一个匿名函数,并传入对应的参数。
function createFunctions(){
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
this对象的问题 :匿名函数的执行环境具有全局性,因此其this对象通常指向window。
解决办法 :使用闭包,可以让匿名函数的this对象指向特定的对象。例如:
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that. name;
};
}
};
alert( object. getNameFunc()()); //"My Object"
使用that
获取目标对象的this
,然后在匿名函数使用that
。
7.3 块级作用域
7.4 私有变量
严格来讲,JavaScript中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
简单来说就是通过作用域链的特性来隐藏不应该被直接修改的数据,例如:
function Person(name){
this. getName = function(){
return name;
};
this. setName = function (value) {
name = value;
};
}
var person = new Person(" Nicholas");
alert( person. getName()); //"Nicholas"
person. setName(" Greg");
alert( person. getName()); //"Greg"
此外还支持使用闭包来实现单例模式,需要时浏览 7.4.3 模块模式。
第8章 BOM
8.1 window对象
window对象也是ECMAScript的global对象,全局作用域中的变量、函数都会变成window对象的属性和方法。通过查询window对象,可以知道某个可能未声明的变量是否存在,例如:
//这里会抛出错误,因为oldValue未定义
var newValue = oldValue;
//这里不会抛出错误,因为这是一次属性查询
//newValue的值是undefined
var newValue = window. oldValue;
8.1.6 超时调用和间歇调用
一般认为,使用超时调用来模拟间歇调用的是一种最佳模式。在开发环境下,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动。而像前面示例中那样使用超时调用,则完全可以避免这一点。所以,最好不要使用间歇调用。
本章个人认为不是重点。
第9章 客户端检测
9.1 能力检测
最常用也最为人们广泛接受的客户端检测形式是能力检测(又称特性检测)。能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。采用这种方式不必顾及特定的浏览器如何如何,只要确定浏览器支持特定的能力,就可以给出解决方案。
要理解能力检测,首先必须理解两个重要的概念。如前所述,第一个概念就是先检测达成目的的最常用的特性。对前面的例子来说,就是要先检测document.getElementById(),后检测document.all。先检测最常用的特性可以保证代码最优化,因为在多数情况下都可以避免测试多个条件。第二个重要的概念就是必须测试实际要用到的特性。一个特性存在,不一定意味着另一个特性也存在。即:不要通过一个特性是否存在来判断浏览器类型。
一般来说,使用typeof
操作符来进行能力检测(判断方法是不是函数),但因为typeof
在IE中存在行为不标准的情况,因此建议使用下面的函数来测试任何对象的某个特性是否存在:
//作者: Peter Michaux
function isHostMethod(object, property) {
var t = typeof object[property];
return t==' function' || (!!(t==' object' && object[property])) || t==' unknown';
}
9.2 怪癖检测
与能力检测类似,怪癖检测(quirksdetection) 的目标是识别浏览器的特殊行为。但与能力检测确认浏览器支持什么能力不同,怪癖检测是想要知道浏览器存在什么缺陷(“怪癖”也就是bug)。一般来说,“怪癖”都是个别浏览器所独有的,且通常被归为bug。
建议仅检测那些对你有直接影响的“怪癖”,而且最好在脚本一开始就执行此类检测,以便尽早解决问题。
第10章 DOM
本章主要讲述什么是DOM,如何用Javascript访问、操作DOM。上课已经涉及,此部分略读。
DOM由各种节点构成,简要总结如下。
最基本的节点类型是Node,用于抽象地表示文档中一个独立的部分;所有其他类型都继承自Node。
Document类型表示整个文档,是一组分层节点的根节点。在JavaScript中,document对象是Document的一个实例。使用document对象,有很多种方式可以查询和取得节点。
Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。
理解DOM的关键,就是理解DOM对性能的影响。DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题为最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。
第11章 DOM扩展
对DOM的两种主要扩展:Selectors API和HTML5
11.1 Selectors API
根据CSS选择符选择与某个模式匹配的DOM元素。jQuery的核心就是通过CSS选择符查询DOM文档取得元素的引用,从而抛开getElementById()
和getElementByTagName()
。
Selectors API是W3C开发制定的一个标准,致力于让浏览器原生支持CSS查询,从而改善性能。
Selectors API Level 1 的核心是两个方法:querySelector()
和querySelectorAll()
。在兼容的浏览器中,可以通过Document及Element类型的实例调用它们。
11.1.1 querySelector()
querySelector()
方法接收一个CSS选择符,返回与该模式匹配的第一个 元素,如果没有找到匹配的元素,返回null。例:
//取得body元素
var body = document.querySelector(" body");
//取得ID为"myDiv"的元素
var myDiv = document.querySelector("# myDiv");
//取得类为"selected"的第一个元素
var selected = document.querySelector(". selected");
通过Doument类型调用querySelector()
方法时,会在文档元素的范围内查找匹配的元素。而通过Element类型调用querySelector()
方法时,只会在该元素后代元素的范围内查找匹配的元素。
11.1.2 querySelectorAll()
11.2 元素遍历
11.3 HTML5
因为HTML5涉及的面非常广,本节只讨论与DOM节点相关的内容。HTML5的其他相关内容将在本书其他章节中穿插介绍。
11.3.1 与类相关的扩充
getElementByClassName()
:返回带有指定类的所有元素的NodeList,只有位于调用元素子树 中的元素才会返回。
classList属性:方便管理元素的class属性(原来是字符串,修改字符串相对不方便)。使用classList属性的add(value), contains(value), remove(value), toggle(value)
即可非常方便地管理class属性。
11.3.2 焦点管理
相关方法和属性:focus()
:获得焦点, document.activeElement
:获取document中的焦点元素, hasFocus()
判断元素是否获得焦点。
11.3.3 HTMLDocument的变化
readyState
属性:使用document.readyState
来确认文档是否已经加载完。
插入标记:
innerHTML:DOM子树,可读可写。并不是所有元素都支持innerHTML属性。不支持innerHTML的元素有: 、、、、、