花了半个多月的时间,终于又把“JS红宝书”又撸了一遍。
第一次读“JS红宝书”还是2015年初学JS的时候,那时候只是把语法部分读了一遍,还有一些浏览器相关知识做了下了解,大概也就读了半本的样子,
就开始了用JS进行开发了,在成长的道路上遇见了JQuery,当时真的是感觉到JQuery太友好了,慢慢放下了原生开发。
现在呢,更多的时候是在用框架进行开发,越来越觉得自己的JS基础很缺乏,然后就开启了“JS红宝书”二刷之路。
下面就把书中自己觉得重要的、没有掌握的知识整理出来。因为我觉得还是会三刷“JS红宝书”,希望把这本700多页的书越读越薄,勉励。
章节
- 在HTML中使用JavaScript
- 基本概念
- 变量、作用域和内存问题
- 引用类型
- 面向对象的程序设计
- 函数表达式
- BOM
- DOM
- DOM 扩展
- DOM2 和 DOM3
在HTML中使用JavaScript
async
加载外部脚本文件,通知浏览器立即下载,异步执行。
noscript 元素
noscript
标签显示条件:
- 浏览器不支持脚本
- 浏览器支持脚本,但是脚本被禁用
基本概念
语法
标识符
- 第一个字符必须是一个字母、下划线或者一个美元符号
- 其他字符可以是字母、下划线、美元或者数字
严格模式
支持严格模式的浏览器包括:IE10+、Firefox4+、Safari 5.1+、Opera 12+和Chrome。
数据类型
undefined
对未初始化的变量执行typeof
操作会返回undefined
值,而对于未声明的变量执行typeof
操作同样会返回undefined
值。
null
typeof null // -> object
undefined值派生自null
值。
console.log(null == undefind) // -> true
isFinite()
测试一个数值是不是无穷值。
Number.NEGATIVE_INFINITY
:负无穷
Number.POSITION_INFINITY
:正无穷
NaN
在ECMAScript中,任何数值除以0会返回NaN
。
isNaN()
接受一个参数,确定这个参数是否"不是数值"。
数值转换
Number()
- 如果是
null
,返回0
- 如果是
undefined
,返回NaN
parseInt()
在ES5 中不支持解析八进制的能力。
parseInt('070'); // -> 70 not 56
通过第二个参数,指定转换基数(进制)默认十进制。
字符串
- ECMAScript中的字符串是不可变的
toString()
在调用数值的toString
方法,可以传递一个参数:输出数值的基数。没有toString
方法的则返回该值的字面量
var num = 10;
console.log(num.toString(2)); // -> '1010'
object类型
Object类型的属性方法:
- constructor
- hasOwnProperty(propertyName)
- isPrototypeOf(obj)
- propertyIsEnumerable(propertyName)
- toLocalString()
- toString()
- valueOf()
操作符
-
++
or--
前置与后置的区别
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2; // 21
var num4 = num1 + num2; // 21
var num5 = 2;
var num6 = 20;
var num7 = num5-- + num6; // 22
var num8 = num5 + num6; // 21
- 一元加操作符用于强制类型转换,隐式
Number()
效果
for-in 语句
for-in
语句是一种精确的迭代语句,可以用来枚举对象的属性。
通过for-in
循环输出的属性名的顺序是不可预测的。
如果要迭代的对象的变量值为null
或undefined
,for-in
语句会抛出错误。ES5更正了这一行为,不再抛出错误,只是不再执行循环体。
建议:在是使用for-in
循环之前,先检查对象值是不是null
或者undefined
。
变量、作用域和内存问题
基本类型和引用类型
复制变量值
- 复制基本类型值,这两个变量相互独立,互不影响。
- 复制引用类型(对象),值引用是一个指针,改变其中一个对象,会影响另一个对象。
传递参数
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
以上代码中创建一个对象,并将其保存在了变量person
中。然后,这个变量被传递到setName()
函数中之后就被复制给了obj
。在这个函数部,obj
和person
引用的是同一个对象。换句话说,即使这个变量是按值传递的,obj
也会按引用来访问同一个对象。于是,当在函数内部为obj
添加name
属性后,函数外部的person
也将有所反映;因为person
指向的对象在堆内存中只有一个,而且是全局对象。有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们再看一看下面这个经过修改的例子:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
这个例子与前一个例子的唯一区别,就是在setName()
函数中添加了两行代码:一行代码为obj
重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name
属性。在把person
传递给setName()
后,其name
属性被设置为"Nicholas"。然后,又将一个新对象赋给变量obj
,同时将其name
属性设置为"Greg"。如果person
是按引用传递的,那么person
就会自动被修改为指向其name
属性值为"Greg"的新对象。但是,当接下来再访问person.name
时,显示的值仍然是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj
时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
检测类型
虽然在检测基本数据类型时typeof
是非常得力的助手,但在检测引用类型的值时,这个操作符的用处不大。通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此,ECMAScript提供了instanceof
操作符。
延长作用域
-
try-catch
语句中的catch
块 -
with
语句
小结
- 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
- 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
- 引用类型的值是对象,保存在堆内存中;
- 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
引用类型
Array类型
检测数组
if (value instanceof Array) {
}
ECMAScript5新增了 Array.isArray()
方法
if (Array.isArray(value)) {
}
sort方法
该方法有缺陷,sort()
方法会调用每个数组项的toString()
转型方法,然后比较字符串进行排序。
var values = [0, 1, 5, 10, 15];
values.sort();
alert(values); //0,1,10,15,5
因此sort()
方法接受一个比较函数作为参数。
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); //0,1,5,10,15
splice方法
splice
方法始终返回一个数组,该数组包含了从原始数组中删除的项。
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项
迭代方法
ECMAScript5为数组定义了5个迭代方法。
-
every()
: 对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。 -
filter()
: 对数组中的每一项运行给定函数,返回该函数会返回true 的项组成的数组。 -
forEach()
: 对数组中的每一项运行给定函数。这个方法没有返回值。 -
map()
: 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。 -
some()
: 对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
归并方法
ECMAScript 5 还新增了两个归并数组的方法。
reduce()
reduceRight()
reduce()
和reduceRight()
的函数接收4 个参数:前一个值、当前值、项的索引和数组对象。
var values = [1, 2, 3, 4, 5];
var sum = values.reduce((prev, cur, index, array) => {
return prev + cur;
});
console.log(sum);
RegExp类型
正则表达式中的元字符
( [ { \ ^ $ | ) ? * + . ] }
注:匹配元字符必须转义
RegExp
构造函数
接受两个参数: 一个是要匹配的字符串模式,另一个是可选的标志字符串。
var pattern1 = /[bc]at/i;
// 等价于
var pattern2 = new RegExp('[bc]at', 'i');
注:由于RegExp构造函数的模式是字符串,所以在某些情况下要对字符串进行双重转义,所有元字符都必须双重转义。例如\n
在字符串中被转义为\\n
,而在正则表达式字符串中就会变成\\\\n
。
RegExp
实例方法
exex()
方法
该方法是专门为捕获组而设计的。exec()
接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null
。返回的数组虽然是Array 的实例,但包含两个额外的属性:index
和input
。其中,index
表示匹配项在字符串中的位置,而input
表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); // 0
alert(matches.input); // "mom and dad and baby"
alert(matches[0]); // "mom and dad and baby"
alert(matches[1]); // " and dad and baby"
aler t(matches[2]); // " and baby"
对于exec()
方法而言,即使在模式中设置了全局标志(g
),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用exec()
将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用exec()
则都会在字符串中继续查找新匹配项。
test()
方法
接受一个字符串参数。在模式与该参数匹配的情况下返回true
;否则,返回false
。
var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
alert("The pattern was matched.");
}
RegExp实例继承的toLocaleString()
和toString()
方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。
var pattern = new RegExp("\\[bc\\]at", "gi");
alert(pattern.toString()); // /\[bc\]at/gi
alert(pattern.toLocaleString()); // /\[bc\]at/gi
Function类型
函数声明与函数表达式
解析器会率先读取函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
// ok
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
// unexpected identifier(意外标识符)
alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
};
注:要访问函数的指针而不执行函数的话,必须去掉函数名后的那对圆括号。
函数内部属性
arguments
this
arguments
具有一个callee
属性,该属性是一个指针,指向拥有这个arguments
对象的函数。
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}
等价于
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)
}
}
达到一种解耦的效果。
ECMAScript 5也规范了一个函数对象属性:caller
(看着很像callee
),这个属性中保存着调用当前函数的函数的引用,如果实在全局作用域中调用当前函数,它的值为null
。
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer();
inner.caller
指向outer()
。为了实现更松散的耦合,也可以通过argument.callee.caller
来访问相同的信息。
function outer() {
inner();
}
function inner() {
alert(arguments.callee.caller);
}
outer();
注:当函数在严格模式下运行时,访问arguments.callee
会导致错误。ECMAScript 5 还定义了arguments.caller
属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是undefined
。定义这个属性是为了分清arguments.caller
和函数的caller
属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。
严格模式还有一个限制:不能为函数的caller
属性赋值,否则会导致错误。
函数属性和方法
每个函数都包含两个属性:
-
length
: 表示函数希望接收的命名参数的个数 -
prototype
: 保存实例方法
每个函数都包含两个非继承而来的方法:
apply()
-
call()
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this
对象的值。首先,apply()
方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array
的实例,也可以是arguments
对象。
call()
方法与apply()
方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()
方法而言,第一个参数是this
值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()
方法时,传递给函数的参数必须逐个列举出来。
注:在严格模式下,未指定环境对象而调用函数,则this
值不会转型为window
。除非明确把函数添加到某个对象或者调用apply()
或call()
,否则this 值将是undefined
。
在非严格模式下,call
、apply
的第一个参数传递为null
或undefined
时,函数体内的this
会指向默认的宿主对象,在浏览器中则是window
。
ECMAScript 5定义了一个方法bind()
,这个方法会创建一个函数的实例,其this
值会被绑定到传给bind()
函数的值。
基本包装类型
使用new
调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。
var value = '25';
var number = Number(value); // 转型函数
console.log(typeof number); // 'number'
var obj = new Number(value); // 构造函数
console.log(typeof obj); // 'object'
Number类型
Number类型的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"
String类型
字符方法
charAt()
charCodeAt()
var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"
alert(stringValue.charCodeAt(1)); //输出字符编码"101"
字符串操作方法
concat()
slice()
substr()
substring()
这些方法对原字符均没有任何影响。
var stringValue = "hello ";
var result = stringValue.concat("world", "!");
alert(result); //"hello world!"
var stringValue = "hello world";
alert(stringValue.slice(3)); //"lo world"
alert(stringValue.substring(3)); //"lo world"
alert(stringValue.substr(3)); //"lo world"
alert(stringValue.slice(3, 7)); //"lo w"
alert(stringValue.substring(3,7)); //"lo w"
alert(stringValue.substr(3, 7)); //"lo worl"
// 参数是负值的情况下,它们的行为就不尽相同了。
// 其中,slice()方法会将传入的负值与字符串的长度相加,
// substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0。
// 最后,substring()方法会把所有负值参数都转换为0。
alert(stringValue.slice(-3)); //"rld"
alert(stringValue.substring(-3)); //"hello world"
alert(stringValue.substr(-3)); //"rld"
alert(stringValue.slice(3, -4)); //"lo w"
alert(stringValue.substring(3, -4)); //"hel"
alert(stringValue.substr(3, -4)); //""(空字符串)
字符串位置方法
indexOf()
lastIndexOf()
两个方法的第二个参数,表示从字符串中哪个位置开始搜索。
trim()方法
ECMAScript 5方法
字符串转换大小写方法
toLowerCase()
toLocaleLowerCase()
toUpperCase()
toLocaleUpperCase()
字符串的模式匹配方法
match()
search()
replace()
split()
match()
方法,在字符串上调用这个方法,本质上和调用RegExp
的exec()
方法相同。match()
方法只接受一个参数,要么是一个正则表达式,要么是一个RegExp对象。
var text = 'cat, bat, sat, fat';
var pattern = /.at/;
// 等价于 pattern.exec(text)
var matches = text.match(pattern);
alert(matches.index); //0
alert(matches[0]); //"cat"
alert(pattern.lastIndex); //0
search()
方法的参数与match()
方法相同,该方法返回字符串中第一个匹配项的索引,没有匹配项返回-1
;个人认为serch()
就是正则版的indexOf()
。
var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
aler t(pos); //1
ECMAScript提供了replace()
方法,该方法接受两个参数,第一个参数可以是RegExp对象或者是一个字符串,第二个参数可以是一个字符串或者一个函数。
var text = "cat, bat, sat, fat";
var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"
result = text.replace(/at/g, "ond");
aler t(result); //"cond, bond, sond, fond"
| 字符序列 | 替换文本 |
| : - : | : -- : |
| $$
| $
|
| $&
| 匹配整个模式的子字符串。RegExp.lastMatch |
| $'
| 匹配子字符串之前的字符串。RegExp.leftContext |
| $
` | 匹配的子字符串之后的字符串。 RegExp.rightContext |
| $n
| 匹配第n个捕获组的子字符串 n: 0~9 |
| $nn
| 匹配第nn个捕获组的子字符串 nn: 01~99 |
var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)
split()
方法可以基于指定的分隔符(字符串 or RegExp对象)将一个字符串分割成多个子字符串,并将结构放在一个数组中。可以接受可选的第二个参数,用于指定数组的大小。
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); //["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); //["red", "blue"]
var colors3 = colorText.split(/[^\,]+/); //["", ",", ",", ",", ""]
localeCompare()方法
比较两个字符串,并返回下列值中的 一个:
- 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数
- 如果字符串等于字符串参数,则返回0;
- 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数
var stringValue = "yellow";
alert(stringValue.localeCompare("brick")); //1
alert(stringValue.localeCompare("yellow")); //0
alert(stringValue.localeCompare("zoo")); //-1
fromCharCode()方法
这个方法的任务是接收一个或多个字符编码,然后将它们转换成一个字符串。相当于charCodeAt()
反操作。
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
Math 对象
min()
max()
ceil()
floor()
round()
random()
面向对象的程序设计
理解对象
属性类型
数据类型
-
[[Configurable]]
: 表示能否通过delete
删除属性从而重新定义属性,能够修改属性的特性,或者能否把属性修改为访问器属性 -
[[Enumerable]]
: 表示能否通过for-in
循环返回属性 -
[[Writable]]
: 表示能否修改属性的值 -
[[Value]]
: 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值undefined
要修改属性默认的特性,必须使用ECMAScript 5的Object.defineProperty()
方法。这个方法接受三个参数:属性所在对象,属性名和一个描述符对象。其中描述符对象的属性值必须是:configurable
、enumerable
、writable
和value
。设置其中一个或多个。
var person = {};
Object.defineProperty(person, 'name', {
writable: false,
value: 'Yeaseon'
});
Object.defineProperty()
方法不能对configurable: false
的对象进行修改。
访问器属性
-
[[Configurable]]
: 表示能否通过delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。 -
[[Enumerable]]
: 表示能否通过for-in
循环返回属性。 -
[[Get]]
: 在读取属性时调用的函数,默认undefined
-
[[Set]]
: 在写入属性时调用的函数,默认undefined
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); //2
读取属性的特性
ECMAScript 5的Object.getOwnPropertyDescriptor()
方法,可以取得给定属性的描述符。该方法接收两个参数:属性所在的对象和要读取器描述符的属性名称,返回值是对象。
创建对象
工厂模式
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
构造函数模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
原型模式
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
Person.prototype.constructor
会指向Person
,person1
并没有直接连接到构造函数Person
。
- 可以通过
isPrototypeOf()
方法来确定对象之间是否存在原型关系。从本质上讲,[[Prototype]]
指向调用isPrototypeOf()
方法的对象Person.prototype
,则会返回true
。
alert(Person.prototype.isPrototypeOf(person1)); //true
在ECMAScript 5增加了Object.getPrototypeOf()
方法,该方法返回[[Prototype]]
的值。
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
注: 虽然可以通过对象实例person1
访问保存在原型中的值,但却不能重写原型中的值。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg"; //实质是在实例上增加一个name属性
alert(person1.name); //"Greg"——来自实例
alert(person2.name); //"Nicholas"——来自原型
可以通过delete
删除实例属性,从而继续访问原型中的属性。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型
-
hasOwnProperty()
方法可以检测一个属性是不是存在于实例,是则返回true
。
-
in
操作符
(prop in obj)通过in
操作符可以判定对象是否有该属性,不论是本身含有还是原型含有,都返回true
。
可以通过in
配合hasOwnProperty()
确定该属性是存在于对象中还是原型中:
function detectProperty(obj, name) {
if (name in obj) {
obj.hasOwnProperty(name) ? '在对象中' : '在原型中';
} else {
console.log('不含有该属性');
}
}
-
ECMAScript 5
Object.keys()
方法可以取得对象上所有可枚举的实例属性。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age"
-
Object.getOwnPropertyNames
会得到所有实例属性,不论是否可枚举。
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"
简化Person.prototype
写法:
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
这样写有一个缺陷,constructor
属性则会等于Object
,我们需要手动设置constructor
。
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
但这同时也会导致constructor
的[[Enumerable]]
特性变成了true
,默认情况下是false
。再修改下写法:
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
原型重写会导致构造函数与最初原型之间的联系切断。
function Person(){
}
var friend = new Person();
Person.prototype = { //重写
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};
friend.sayName(); //error
结合使用构造函数和原型模式
用构造函数模式定义实例属性,用原型模式定义方法和共享属性。
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);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
继承
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型的内部指针。
原型链
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType () {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
var instance = new SubType();
console.log(instance.getSuperValue()); // true
instanceof
操作符
用来确定原型和实例之间的关系。
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
第二种方式就是isPrototypeOf()
方法,只要原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
函数表达式
由于有声明提升的存在,定义函数不要放在条件表达式中
if (condition) {
function sayHi () {
console.log('Hi');
}
} else {
function sayHi () {
console.log('Yo');
}
}
在ECMAScript中属于无效语法,在不同浏览器中修正的做法并不一致。推荐的写法,如下:
var sayHi;
if (condition) {
sayHi = function () {
console.log('Hi');
}
} else {
sayHi = function () {
console.log('Yo');
}
}
这种函数表达式不存在声明提升,所以OK。
递归
函数作用域链
当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用arguments
和其他命名参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位...,直到作用域终点的全局执行环境。
闭包
function createFunctions () {
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
function createFunction () {
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
。
var name = 'The window';
var obj = {
name: 'my object',
getNameFunc: function () {
return function () {
return this.nam;
}
}
}
console.log(obj.getNameFunc()()); // The Window (非严格模式)
模仿块级作用域
块级作用域
(function () {
// 这里是块级作用域
})();
BOM
window 对象
全局作用域
抛开全局变量会成为window
对象的属性不谈,定义全局变量与在window
对象上直接定义属性还是有一点差别:全局变量不能通过delete
操作符删除,而直接定义在window
对象上的定义的属性可以。
窗口位置
获得窗口左边和上边的位置。
var leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY;
Firefox支持screenX
和screenY
,其他浏览器均支持screenLeft
、screenTop
。
但是还是需要注意一个问题:在IE
Opera
中,screenLeft screenTop
保存的的是可见区域的距离,也就是我们浏览器中不包含工具栏的区域与屏幕的距离;在Chrome
、Firefox
和Safari
中screenY
或screenTop
返回的是整个浏览器窗口相对于屏幕坐标的值。
窗口大小
IE9+、Firefox、Safari、Opera和Chrome均提供了4个属性innerWidth
、innerHeight
、outerWidth
和outerHeight
。
- IE9+、Safari和Firefox中,
outerWidth
和outerHeight
返回浏览器窗口本身的尺寸,而innerWidth
和innerHeight
则表示该容器中页面视图区的大小(减去边框宽度) - Chrome中,
inner*
和outer*
返回相同的值,即视口大小而非浏览器窗口的大小。 - 在IE、Firefox、Safari、Opera和Chrome中,都能通过
document.documentElement.clientWidth
和document.documentElement.clientHeight
中保存了页面视口信息。
获取页面视口大小
var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if (typeof pageWidth != 'number') {
if (document.compatMode == 'CSS1Compat') { // 浏览器标准模式
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else { // IE6 混杂模式
pageWidth = document.body.clientWidth;
pageHeight = document.doby.clientHeight;
}
}
-
resizeTo()
接受浏览器窗口的新宽度和新高度 -
resizeBy()
接受新窗口与原窗口的宽度和高度差。
这两个方法可能被浏览器禁用。
导航
如果是浏览器内置的屏蔽程序组织的弹出窗口,那么window.open()
很可能会返回null
。
var newWindow = window.open('https://www.google.com.hk', '_blank');
if (newWindow == null) {
console.log('The popup was blocked!');
}
如果是浏览器扩展或其他程序组织的弹出窗口,那么window.open()
通常会抛出一个错误。
var blocked = false;
try {
var newWindow = window.open('https://www.google.com.hk', '_blank');
if (newWindow == null) {
blocked = true;
}
} catch (ex) {
blocked = true;
}
if (blocked) {
console.log('The popup was blocked');
}
location 对象
location
对象的属性
hash
host
-
hostname
:与host
不同的是,不带端口号 href
-
pathname
: 返回URL中的目录和(或)文件名 port
protocol
-
search
:返回URL的查询字符串,这个字符串?
开头
navigator 对象
location
对象的属性
-
appCodeName
: 浏览器的名称,通常都是Mozilla -
appMinorVersion
:此版本信息 -
appName
: 完整的浏览器名称 -
appVersion
:浏览器的版本 -
buildID
:浏览器编译版本 -
cookieEnabled
:表示cookie
是否可用 -
cpuClass
:客户端计算机中使用的CPU类型 -
javaEnabled()
:表示当前浏览器中是否启用了java -
language
: 浏览器的主语言 -
mimeTypes
:浏览器中注册的MIME类型数组 -
onLine
:表示浏览器是都连接到因特网 -
oscpu
:客户端计算机的操作系统或使用的CPU -
platform
:浏览器所在的系统平台 -
plugins
:浏览器中安装的插件信息的数组 -
preference()
:设置用户的首选项 -
systemLanguage
:操作系统的语言 -
userAgent
:浏览器的用户代理字符串
DOM
节点层次
Node类型
每个节点都有一个nodeType
属性,用于表明节点的类型。
Node.ELEMENT_NODE(1)
Node.ATTRIBUTE_NODE(2)
Node.TEXT_NODE(3)
Node.CDATA_SECTION_NODE(4)
Node.ENTITY_REFERENCE_NODE(5)
Node.ENTITY_NODE(6)
Node.PROCESSING_INSTRUCTION_NODE(7)
Node.COMMENT_NODE(8)
Node.DOCUMENT_NODE(9)
Node.DOCUMENT_TYPE_NODE(10)
Node.DOCUMENT_FRAGMENT_NODE(11)
Node.NOTATION_NODE(12)
为了确保跨浏览器兼容,将nodeType
属性与数字值进行比较:
if (someNode.nodeType == 1) {
console.log('Node is an element');
}
-
nodeName
属性
if (someNode.nodeType == 1) {
var value = someNode.nodeName; // nodeName的值是元素的标签名
}
节点关系
-
childNodes
属性
每个节点都有一个childNodes
属性,其中保存着一个NodeList
对象,该对象是一种类数组对象。
-
parentNode
属性
每个节点都有一个parentNode
属性,该属性指向文档树中的父节点。包含在childNodes
列表中的每个节点相互都是兄弟节点。使用previousSibling
和nextSibling
属性,可以访问其他兄弟节点。
注:列表中第一个节点的previousSibling
属性值为null
,同理列表中最后一个节点的nextSibling
属性也是null
。父节点的firstChild
和lastChild
属性分别指向其childNodes
列表中的第一个和最后一个节点。如果不存在则为null
。
hasChildNodes()
方法在节点包含一个或多个子节点的情况下返回true
,比查询childNodes.length
更简便。
最后一个属性ownerDocument
,该属性指向表示整个文档的文档节点(root),直接返回根节点不需要一层层向上回溯。
操作节点
appendChild()
用于向childNodes
列表的末尾添加一个节点。
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true
任何DOM节点不可能同时出现在多个位置。
//someNode 有多个子节点
var returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); //false
alert(returnedNode == someNode.lastChild); //true
insertBefore()
把节点放在指定位置,该方法接受两个参数:要插入的节点和作为参考的节点。插入节点后,被插入的节点会变成参照节点的前一个兄弟节点。参照节点是null
的话,insertBefore
与appendChild
执行相同的操作,都插入列表末尾。
//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true
//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true
//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true
replaceChild()
替换节点,接受两个参数:要插入的节点和要替换的节点。
//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
removeChild()
移除节点,接受一个参数:要被移除的节点。
//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild);
//移除最后一个子节点
var formerLastChild = someNode.removeChild(someNode.lastChild);
cloneNode()
复制节点,接受一个布尔值,表示是否深复制。复制后返回的节点没有父节点,可以通过插入等操作手动指定。
var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(IE < 9)或7(其他浏览器)
var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0
注:cloneNode
方法不会复制DOM节点的js
属性。IE存在一个bug,它会复制事件处理程序。
normalize()
稍后讨论
以上方法的返回值,都是被操作的节点。
Document类型
Document
节点具有下列特征:
-
nodeType
的值为9 -
nodeName
的值为#document
-
nodeValue
的值为null
-
parentNode
的值为null
-
ownerDocument
的值为null
- 其子节点可能是一个
DocumentType
(最多一个)、Element
(最多一个)、ProcessingInstruction
或Comment
// 通过`documentElement`属性访问<html>元素
var html = document.documentElement;
// 访问 <body> 元素
var body = document.body;
// <!DOCTYPE>
var doctype = document.doctype;
// <title>
var title = document.title;
// 完整 url
var url = document.URL;
// domain 域名
var domain = document.domain;
// 取得来源页面的URL(也就是导航到这页的页面)
var referrer = document.referrer;
查找元素的方法:
document.getElementById()
document.getElementsByTagName()
document.getElementsByName()
文档写入:
document.write()
-
document.writeln()
在字符串尾加换行符(\n)
<script type="text/javascript">
document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");
</script>
Element类型
Element
类型提供了对元素标签名、子节点及特性的访问。
-
nodeType
的值为1 -
nodeName
的值为元素的标签名 -
nodeValue
的值为null
-
parentNode
可能是Document
或Element
- 其子节点可能是
Element
、Text
、Comment
、ProcessingInstruction
、CDATASection
或EntityReference
访问元素的标签名,可以使用nodeName
属性,也可以使用tagName
属性,后者更直观。
<div id="myDiv"></div>
var div = document.getElementById("myDiv");
alert(div.tagName); //"DIV"
alert(div.tagName == div.nodeName); //true
操作特性的方法:
getAttribute()
setAttribute()
removeAttribute()
attributes
属性
Element
类型是使用attributes
属性的唯一一个DOM节点属性。attributes
属性包含一个NamedNodeMap
。元素的每一个特性都由一个Attr
节点表示,每个节点都保存在NamedNodeMap
对象中。
NamedNodeMap
对象的方法:
-
getNamedItem(name)
:返回nodeName
属性等于name
的节点 -
removeNamedItem(name)
:从列表中移除nodeName
属性等于name
的节点 -
setNamedItem(node)
:向列表中添加节点,以节点的nodeName
属性为索引 -
item(pos)
:返回位于数字pos
位置处的节点
attributes
属性中包含一系列节点,每个节点的nodeName
就是特性的名称,而节点nodeValue
就是特性的值。
var id = element.attributes.getNamedItem('id').nodeValue;
// 简写
var id = element.attributes['id'].nodeValue;
创建元素
document.createElement()
方法可以创建新元素,这个方法接受一个参数(标签名)
var div = document.createElement('div');
Text类型
文本节点由Text
类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML代码。
-
nodeType
的值为3 -
nodeName
的值为#text
-
nodeValue
的值为节点所包含的文本 -
parentNode
是一个Element
操作节点中的文本:
-
appendData(text)
:将text
添加到节点的末尾 -
deleteData(offset, count)
:从offset
指定的位置开始删除count
个字符 -
insertData(offset, text)
:在offset
指定的位置插入text
-
replaceData(offset, count, text)
:用text
替换从offset
指定的位置开始到offset+count
为止的文本 -
splitText(offset)
:从offset
指定的位置将当前文本分成两个文本节点 -
substringData(offset, count)
:提取从offset
指定的位置开始到offset+count
为止处的字符串。
在向DOM文档中插入文本之前,应该先对其进行HTML
编码
创建文本节点
document.createTextNode()
var textNode = document.createTextNode("<strong>Hello</strong> world!");
DOM 操作技术
使用函数实现加载外部JS
文件
function loadScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.body.appendChild(script);
}
loadScirpt('xx.js');
IE将<script>
视为一个特殊的元素,不允许DOM访问其子节点。不过可以使用<script>
元素的text
属性指定JS
代码。
操作表格
// create table
var table = document.createElement('table');
table.border = 1;
table.width = '100%';
// create tbody
var tbody = document.createElement('tbody');
table.appendChild(tbody);
// create row1
var row1 = document.createElement('tr');
tbody.appendChild(row1);
var cell1_1 = document.createElement('td');
cell1_1.appendChild(document.createTextNode('Cell 1,1'));
row1.appendChild(cell1_1);
var cell2_1 = document.createElement('td');
cell2_1.appendChild(document.createTextNode('Cell 2,1'));
row1.appendChild(cell2_1);
// create row2
var row2 = document.createElement('tr');
tbody.appendChild(row2);
var cell1_2 = document.createElement('td');
cell1_2.appendChild(document.createTextNode('Cell 1,2'));
row1.appendChild(cell1_2);
var cell2_2 = document.createElement('td');
cell2_2.appendChild(document.createTextNode('Cell 2,2'));
row1.appendChild(cell2_2);
document.body.appendChild(table);
DOM 扩展
选择符 API
-
querySelector()
方法
querySelector()
方法接受一个CSS选择符,返回与该模式匹配的第一个元素,若没有,返回null
。
可以通过Document
类型调用,也可以通过Element
类型调用,后者只会在该元素后代元素的范围内查找匹配的元素。
-
querySelectorAll()
方法
querySelectorAll()
方法返回的是所有匹配的元素,是一个NodeList
实例。
-
matchesSelector()
方法
为Element
类型新增的一个方法,接受一个参数CSS
选择符,如果调用元素与该选择符匹配,返回true
,否则返回false
。
元素遍历
-
childElementCount
:返回子元素(不包含文本节点和注释)的个数 -
firstElementChild
:指向第一个子元素 -
lastElementChild
:指向最后一个子元素 -
previousElementSibling
:指向前一个兄弟元素 -
nextElementSibling
:指向后一个兄弟元素
不同于前面的返回节点的方法。
// 节点版本
var i,
len,
child = element.firstChild;
while(child != element.lastChild){
if (child.nodeType == 1){ //检查是不是元素
processChild(child);
}
child = child.nextSibling;
}
// 元素版本
var i,
len,
child = element.firstElementChild;
while(child != element.lastElementChild){
processChild(child); //已知其是元素
child = child.nextElementSibling;
}
HTML5
-
getElementsByClassName()
方法 -
classList
属性,这个属性是新集合类型DOMTokenList
的实例。add(value)
contains(value)
remove(value)
toggle(value)
div.classList.remove("user");
焦点管理
document.activeElement
属性,始终会引用DOM中前端获得了焦点的元素。
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
document.hasFocus()
方法,可以确定文档是否获得了焦点。
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true
HTMLDocument
的变化
+ `readyState`属性有两个值,`loading`和`complete`
if (document.readyState == 'complete') {
// 加载完成
}
document.charset
字符集属性
data-
自定义数据属性
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
var div = document.getElementById("myDiv");
//取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;
//设置值
div.dataset.appId = 23456;
div.dataset.myname = "Michael";
innerHTML
属性
在读模式下,innerHTML
属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应
的HTML 标记。在写模式下,innerHTML
会根据指定的值创建新的DOM树,然后用这个DOM树完全
替换调用元素原先的所有子节点
outerHTML
属性
在读模式下,outerHTML
返回调用它的元素及所有子节点的HTML 标签。在写模式下,outerHTML
会根据指定的HTML 字符串创建新的DOM 子树,然后用这个DOM子树完全替换调用元素。
insertAdjacentHTML()
方法
插入元素的新增方法,接受两个参数,插入的位置和要插入的HTML文本,第一个参数的值:
'beforebegin'
'afterbegin'
'beforeend'
'afterend'
//作为前一个同辈元素插入
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");
//作为第一个子元素插入
element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");
//作为最后一个子元素插入
element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");
//作为后一个同辈元素插入
element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");
scrollIntoView()
方法
scrollIntoView
方法可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果这个方法传入true
作为参数,或者不传参数,那么窗口滚动之后就会让调用元素的顶部与视口顶部 尽可能平齐,如果传入false
,调用元素会尽可能全部出现在视口中,不过顶部不一定平齐。
// 让元素可见
document.form[0].scrollIntoView();
专有扩展
插入文本
-
innerText
属性 -
outerText
属性
滚动
-
scrollIntoViewIfNeeded(alignCenter)
:只有在当前元素不可见的情况下,才滚动浏览器或窗口或容器元素最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。 -
scrollByLines(lineCount)
:将元素的内容滚动指定的行高,lineCount
值可以是正值,也可以是负值。 -
scrollByPages(pageCount)
:将元素的内容滚动指定的页面高度,具体高度由元素的高度决定。
scrollIntoView()
和scrollIntoViewIfNeeded()
的作用对象是元素的容器,而scrollByLines()
和scrollByPages()
影响的则是元素自身。
//在当前元素不可见的时候,让它进入浏览器的视口
document.images[0].scrollIntoViewIfNeeded();
//将页面主体往回滚动1 页
document.body.scrollByPages(-1);
DOM2 和 DOM3
样式
元素大小
偏移量
-
offsetHeight
:元素在垂直方向上占用的空间大小。包括元素的高度,(可见的)水平滚动条的高度,上边框高度和下边框高度 -
offsetWidth
:元素在水平方向上占用的空间大小。包括元素的宽度,(可见的)垂直滚动条的宽度,左边框宽度和右边框宽度 -
offsetLeft
:元素的左外边框至包含元素的左内边框之间的像素距离。 -
offsetTop
:元素的上外边框至包含元素的上内边框之间的像素距离。
其中,offsetLeft
和offsetTop
属性与包含元素有关,包含元素的引用保存在offsetParent
属性中。offsetParent
属性不一定与parentNode
的值相等。
// 元素上偏移
function getElementLeft (ele) {
var actualLeft = ele.offsetLeft;
var current = ele.offsetParent;
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
// 元素左偏移同理
一般来说,页面中所有的元素都被包含在几个<div>
元素中,而这些<div>
元素的offsetParent
又是<body>
元素,所以getElementLeft()
与getElementTop()
会返回与offsetLeft
和offsetTop
相同的值。
客户区大小
-
clientWidth
:元素内容区宽度加上左右内边距宽度 -
clientHeight
: 元素内容区高度加上上下内边距高度
function getViewport(){
if (document.compatMode == "BackCompat"){ // IE7之前
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}
滚动大小
滚动大小,指的是包含滚动内容的元素的大小。有些元素(<html>
),即使没有执行任何代码也能自动添加滚动条;但另外一些元素,则需要通过CSS的overflow
属性设置才能滚动。
-
scrollHeight
:在没有滚动条的情况下,元素内容的总高度 -
scrollWidth
:在没有滚动条的情况下,元素内容的总宽度 -
scrollLeft
:被隐藏在内容区域左侧的像素数,通过设置这个属性可以改变元素的滚动位置 -
scrollTop
:被隐藏在内容区域上方的像素数,通过设置这个属性可以改变元素的滚动位置
scrollWidth
和scrollHeight
主要用于确定元素内容的实际大小。
scrollWidth
和scrollHeight
与clientWidth
和clientHeight
之间的关系?
- Firefox中这两组属性始终相等,但大小代表的是文档内容区域的实际尺寸,非视口尺寸
-
Opera Safari Chrome中这两组属性有区别,其中
scrollWidth
和scrollHeight
等于视口大小,而clientWidth
和clientHeight
等于文档内容区域的大小。 -
IE(在标准模式)中的这两组属性不相等,其中
scrollWidth
和scrollHeight
等于文档内容区域的大小,而clientWidth
和clientHeight
等于视口大小。
通过scrollLeft
和scrollTop
属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位置。在元素尚未被滚动时,这两个属性的值都等于0。如果元素被垂直滚动了,那么scrollTop
的值会大于0,且表示元素上方不可见内容的像素高度。如果元素被水平滚动了,那么scrollLeft
的值会大于0,且表示元素左侧不可见内容的像素宽度。这两个属性都是可以设置的,因此将元素的scrollLeft
和scrollTop
设置为0,就可以重置元素的滚动位置。下面这个函数会检测元素是否位于顶部,如果不是就将其回滚到顶部。
function scrollToTop(element){
if (element.scrollTop != 0){
element.scrollTop = 0;
}
}
确定元素大小
-
getBoundingClientRect()
方法,会返回一个矩形对象,包含left top right bottom
四个属性。这些属性给出了元素在页面中相对于视口的位置。
遍历
NodeIterator
可以使用document.createNodeIterator()
方法创建它的新实例,接受4个参数。
-
root
:想要作为搜索起点的树中的节点 -
whatToShow
:表示要访问哪些节点的数字代码 -
filter
:是一个NodeFilter
对象,或者一个表示应该接受还是拒绝某种特定节点的函数 -
entityReferenceExpansion
:布尔值,表示是否要扩展实体引用。
whatToShow
这个参数的值以常量形式在NodeFilter
类型中定义:
NodeFilter.SHOW_ALL
NodeFilter.SHOW_ELEMENT
NodeFilter.SHOW_ATTRIBUTE
NodeFilter.SHOW_TEXT
NodeFilter.SHOW_CDATA_SECTION
NodeFilter.SHOW_ENTITY_REFERENCE
NodeFilter.SHOW_ENTITYE
NodeFilter.SHOW_PROCESSING_INSTRUCTION
NodeFilter.SHOW_COMMENT
NodeFilter.SHOW_DOCUMENT
NodeFilter.SHOW_DOCUMENT_TYPE
NodeFilter.SHOW_DOCUMENT_FRAGMENT
NodeFilter.SHOW_NOTATION