基本概念
解释型和编译型语言的区别
- 编译型
需要编译器。把高级语言编译为机器码文件,编译期代码检查,执行速度更快,一次编译处处执行,可移植性更好。如:Java,C#等。
- 解释性
需要解释器。解释器直接读入源代码,边解释(编译)边执行。错误只能等到解释器执行的有关代码时才能被发现,再次运行,需要重新解释,速度较慢。如:JS,Python等。
完整的JavaScript的三个部分
核心(ECMAScript)
与Web浏览器没有依赖关系,是基础。定义了:
语法,类型,语句,关键字,保留字,操作符,对象文档对象模型(DOM)
针对XML但经过扩展用于HTML的API。
DOM1级,包括DOM核心(DOM Core)和DOM HTML。浏览器对象模型(BOM)
处理浏览器和框架
严格模式
"use strict"
编译指示,为了不破坏ECMAScript 3语法,一些不确定的行为将得到处理,对于某些不安全的操作也会抛出错误。
局部域定义全局变量
1 | function test(){ |
并不推荐,很难维护。相应变量不会马上就有定义导致混乱。
数据类型
undefined,null,boolean,number,string
typeof 操作符
未定义 - undefined
布尔值 - boolean
字符串 - string
数值 - number
对象或null - object
函数 - function
从技术上来讲,函数在ECMAScript中是对象,不是一种数据类型。然后函数有一些特殊的属性,typeof得到的值也不同于对象
undefined类型
1 | var v1; //声明之后默认取得undefined值 |
null类型
typeof null //返回 object
适用于,准备将变量用于保存对象,那么最好将变量初始化为null,而不是其他值。使用 if(obj != null)来判断是否已经保存对象的引用。
undefined值派生自null值,所以null == undefined 返回ture。
boolean类型
1 | if (true) { //通过,区分大小写 |
Boolean()函数,传入任何参数总会返回Boolean值。规则如下:
类型 | 返回true | 返回false |
---|---|---|
Boolean | true | false |
String | 任何非空字符串 | “”(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
Undefined | N/A(不适用) | undefined |
不传参数,返回false。
if()语句,自动执行Boolean()函数。
Number类型
整数值:
整数值可以以十进制,八进制,十六进制来表示。1
2
3
4var num=070; //前导0,十进制的56
var num=079; //无效,前导0被忽略,解析为79
var num=0xA; //前导0x,十进制的10
var num=0x1z //无效,报错浮点数值:
1
2
3
4
5var num=1.; //解析为整数,1
var num=10.0; //解析为整数,10
简写:
var num=3.12e7; //3.12乘以10的7次方
var num=3.12e-7; //3.12乘以0.1的7次方PS:0.1+0.2 == 0.3
返回false,因为ECMAScript的浮点运算基于IEEE754,存在二进制和十进制的小数点转换问题,并非独此一家。数值范围
1
2
3
4
5alert(Number.MAX_VALUE + Number.MAX_VALUE) //返回Infinity
alert(-1 * (Number.MAX_VALUE + Number.MAX_VALUE)) //返回-Infinity
Number.POSITIVE_INFINITY //保存着Infinity
Number.NEGATIVE_INFINITY //保存着-Infinity
isFinite() //判断是否是有穷的,Infinity返回false,MAX_VALUE返回ture,MAX_VALUE+1返回tureNaN
任何涉及NaN的计算,返回都是NaN。
NaN与任何值都不相等,包括NaN本身。NaN == NaN ,返回false。1
2
3
4
5isNaN(NaN) //true
isNaN(10) //false
isNaN("10"/"") //false ,可被转换成数值10/0
isNaN("blue") //true,不能转换
isNaN(ture/false) //false,可别转换成数值1数值转换
Number(),用于任何数据类型
规则:
Boolean,ture和false分别转换为1和0
数字,直接返回
null,返回0
undefined,返回NaN
字符串:整数,浮点数,对应的十进制。有效的十六进制,等值的十进制整数。空字符串,0;包含其他字符,NaN
对象,先调用valueOf()方法,然后按照前面的规则转换返回的值,如果结果为NaN。再调用toString()方法。然后转换返回的字符串parseInt(),parseFloat(),转换字符串。
String类型
1 | var num=10; |
Object类型
常用属性/方法:
- Constructor:保存着用于创建当前对象的构造函数。
- hasOwnProperty(name):检查给定的属性在当前对象实例中是否存在。
- toString()
- valueOf(),返回对象的字符串,数值或布尔值表示
BOM和DOM中的对象,属于宿主对象。可能不会集成Object。
操作符
乘法
有一个数是NaN,结果是NaN。
Infinity与0相乘,结果是NaN。
Infinity与非0相乘,结果是Infinity或-Infinity,取决于操作数的符号。
如果是Infinity与Infinity相乘,结果是Infinity。
如果一个不是数值,则调用Number()转换为数值后再相乘。
除法
一个数是NaN,结果NaN。
Infinity被Infinity除,结果是NaN。
0被0除,结果NaN。
非零有限数被0除,结果是Infinity或-Infinity,取决于符号。(-1/0)
Infinity被非零数除,结果是Infinity或-Infinity,取决于符号。
一个操作数不是数值,同乘法。
加法
1 | Infinity+Infinity=Infinity |
如果一个操作数是字符串,调用他们的toString()得到字符串(undefined和null调用String()返回字面量),然后拼接。两个都是字符串,则直接拼接。
没有字符串,则先调用Number()转换为数值,再进行加法。
减法
1 | Infinity-Infinity=NaN |
如果一个操作数是字符串,布尔值,null或undefined,则先调用Number()函数转换为数值,再根据规则进行减法。如果转换为NaN,则结果是NaN。
+/- 操作符
对非数值应用一元操作符时,会先执行Number()函数转换,然后在转为正/负数。
1 | +"01" //1 |
比较操作符
两个字符串,比较两个字符串对应的字符编码值。
一个数值,一个非数值,先进行数值转换。
一个对象,则调用valueOf()方法,没有则调用toString()方法。再比较。
1 | "a" >/< 3 //false,先进行转换"a"->NaN |
相等和不相等
默认先进行强制转型,再比较。
字符串/布尔,先转换,再比较。
一个对象,一个不是,则调用对象的valueOf方法。
两个都是对象,则比较是不是同一个对象。
null和undefined是相等的,且比较时都不会执行转换。
特殊情况:
1 | null == undefined //true |
语句
for-in语句
可以用来枚举对象的属性1
2
3
4for (var prop in window) {
var element = window[prop];
console.log(prop + ' ' + element)
}
switch语句
可以使用任何数据类型,case的值可以是常量,变量,或者表达式1
2
3
4
5
6
7
8
9
10
11
12var num = 12;
switch (true) {
case num > 10:
alert(">")
break;
case num < 10:
alert("<")
break;
default:
alert("=")
break;
}
函数
定义时不必制定是否返回值。
严格模式下,不能把函数/参数命名为eval或arguments,不能有连个同名参数。
参数
ECMAScript函数不介意传递进多少个参数,不也在乎参数类型。定义和实际传递的参数没有个数与对应关系的限制。
因为,参数在内部是用一个类似数组的对象(Arguments对象,并不是真的数组)来表示的。而不关心数组中包含哪些参数。1
2
3
4
5name(1) //length 为1
name(1,2) //length 为2
function name() {
alert(arguments[0])
}
命名参数只提供便利,但不是必须的。解析器不会验证命名参数。1
2
3
4function add(num1,num2){
arguments[1]=10;
alert(arguments[0]+num2);
}
- 修改了arguments[1]的值,也就修改了num2,他们的值都会改变。不过它们的内存空间是独立的,但它们的值会同步。
- arguments.callee返回的是当前执行的函数。
- js的函数没有重载,重复定义的函数不论参数列表,后定义的函数会覆盖前面的函数。但可以使用arguments对象来模拟实现。
变量、作用域和内存
基本类型和引用类型
传递参数
ECMAScript所有函数的参数都是按值传递。对于引用类型(对象)来说,不会改变原始引用,只会根据引用改变对象的成员。1
2
3
4
5
6
7
8
9
10var p = { name: 'bily' };
set(p);
alert(p.name)
①function set(p) {
p = {};
p.name = 'alan';
}
②function set(p) {
p.name = 'alan';
}
①,弹出bily。②,弹出alan。
检测类型
1 | typeof: |
执行环境与作用域
每个函数都有自己的执行环境和作用域,但js没有块级作用域。例如if和for等语句。
但是也有例外,例如catch和with语句。
查询标识符
在某个环境中读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。从作用域链的前段开始,向上逐级查询。
局部找到,则搜索停止。否则一直向上追溯到全局环境的变量对象,如果都没有找到,则意味着该变量未声明。
垃圾回收
引用类型
Object类型
Array类型
创建
1 | var colors = ["red", "blue", "green"]; |
检测
arr instanceof Array //true/false
假定单一的全局执行环境,如果网页包含多个框架,则可能出现问题。
为了解决这个问题,ECMAScrit新增了能检测iframes的方法
Array.isArray(arr) //true/false IE9+
转换方法
1 | var colors = ["red", "blue", "green"]; |
栈方法:LIFO
1 | var colors = []; |
队列方法:FIFO
1 | var colors = []; |
排序方法
1 | var values = [0, 1, 5, 10, 15]; |
自定义排序
1 | function compare(value1, value2) { |
操作方法:增,减,替换
concat
返回新构建的数组副本
1
2
3
4var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); //red,green,blue
alert(colors2); //red,green,blue,yellow,black,brownslice
返回当前数组截取的副本
1
2
3
4
5var 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,yellowsplice
删除,插入,替换数组元素。返回删除项组成的数组,直接影响原数组
1
2
3
4
5
6
7
8
9
10
11
12var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1);
alert(colors); //green,blue
alert(removed); //red (数组)
removed = colors.splice(1, 0, "yellow", "orange");
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(数组)
数组的迭代方法
- every(),对每一项运行给定函数,如果该函数对每一项都返回ture,则返回ture
- some(),对每一项运行给定函数,如果该函数对任一项返回ture,则返回ture
- filter(),对每一项运行给定函数,返回该函数会返回ture的项组成的数组
- map(),对每一项运行给定函数,返回每次函数调用的结果组成的数组
- forEach(),对每一项运行给定函数。无返回值。
1 | var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; |
缩小方法
接受4个参数,前一个值,当前值,当前索引,数组对象。函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项。1
2
3
4
5var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
return prev + cur;
});
alert(sum);
reduceRight从反方向执行。
Date类型
创建
1 | var now = new Date() //新创的对象自动获得当前日期时间。 |
格式化
RegExp类型
正则表达式模式
1 | var pattern=/[bc]at/i; |
实例属性
exec()
1 | var text = "mom and dad and baby"; |
[0],与整个模式匹配的字符串
[1][2],与模式中的捕获组(?)匹配的字符串。没有捕获组则不存在。
模式g,不设置则多次调用exec()将始终返回第一个匹配项的信息。设置则每次调用exec()都会在字符串中继续查找剩余的新匹配项。
构造函数属性
1 | var text = "this has been a short summer"; |
Function类型
定义
“函数是对象,函数名是指针”
每个函数都是Function类型的实例,具有属性和方法。函数名不与某个函数绑定,只是一个指向函数对象的指针。
function sum(){…} 等于 var sum=function(){…}
所以JS没有重载,声明同一个函数两次,函数名指向最后赋值的函数对象。
函数声明与函数表达式
1 | ① |
作为值的函数
1 | function createComparisonFunction(propertyName) { |
函数内部属性
arguments.callee,指向拥有arguments对象的函数1
2
3
4
5
6
7
8
9
10
11
12
13function factorial(num){
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num-1) ;
//如果return num*factorial(num-1);,则代码和函数名紧耦合
}
}
var trueFactorial = factorial;
factorial = function(){
return 0;
};
alert(trueFactorial(5)); //120。代码执行不因函数名的指向而改变。
this,引用的是函数所在的执行环境。全局作用域中调用函数时,this引用的是window1
2
3
4
5
6
7
8window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); //blue
caller,保存着调用当前函数的函数的引用,全局作用域中调用当前函数,它为null1
2
3
4
5
6
7function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller); //或inner.caller
}
outer();
函数属性和方法
length,函数定义的命名参数的个数。
function sum(num1,mum2){…} //length为2
apply()和call(),每个函数都包含两个非继承而来的方法,用途都是在特性的作用域中调用函数,可以设置函数体内this对象的值。apply(obj,[ … ])接收数组参数,call(obj,arg1,arg2…)接收连续参数
bind()创建一个函数的实例,this值绑定到传递给bind()函数的参数值。
1 | function sum(num1, num2){ |
扩充函数赖以运行的作用域
1 | window.color = "red"; |
基本包装类型
1 | var s1 = "text"; //string 是基本类型 |
每当读取一个基本类型值得时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。相当于下面的过程
1 | s1 = new String('text'); |
Boolean类型
1 | var falseObject = new Boolean(false); |
Number类型
1 | var numberObject = new Number(10); |
String类型
1 | var stringObject = new String("hello world"); |
字符方法
x.charAt(),x.charCodeAt() //字符编码,x[n]。
字符串操作方法
concat()
接受任意个参数,拼接返回新字符串1
2
3
4
5
6
7
8
9
10
11
12
13
14var stringValue = "hello world";
alert(stringValue.slice(3)); //"lo world" 总长度作为结束位置
alert(stringValue.slice(3, 7)); //"lo w"
alert(stringValue.substring(3)); //"lo world"
alert(stringValue.substring(3,7)); //"lo w"
alert(stringValue.substr(3)); //"lo world"
alert(stringValue.substr(3, 7)); //"lo worl" 第二个参数指定长度
alert(stringValue.slice(-3)); //"rld" 将负的参数与总长度相加
alert(stringValue.slice(3, -4)); //"lo w"
alert(stringValue.substring(-3)); //"hello world" 将所有负参数转为0
alert(stringValue.substring(3, -4)); //"hel" (3,0)=>(0,3 )
alert(stringValue.substr(-3)); //"rld" 将第一个负的参数与总长度相加
alert(stringValue.substr(3, -4)); //"" 将第二个负的参数转为0
字符串位置方法
1 | var stringValue = "hello world"; |
trim()
返回去除字符串两端空格的副本。
大小写转换
1 | alert(stringValue.toUpperCase()); //"HELLO WORLD" |
字符串的模式匹配方法
match(),本质上与调用RegExp的exec()方法相同。唯一参数,正则表达式。
1
2
3
4
5
6
7var text = "cat, bat, sat, fat";
var pattern = /.at/;
var matches = text.match(pattern);
alert(matches.index); //0
alert(matches[0]); //"cat"
alert(pattern.lastIndex); //0search(),由开头向后查找,返回字符串中第一个匹配项的索引,如果没找到,返回-1。唯一参数,正则表达式。
var pos = text.search(/at/); //1
replace(),第一个参数,正则表达式或字符串,第二个参数字符串或者一个函数。
第一个参数为字符串,只替换第一个匹配项
1
2var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"全局替换,使用正则表达式
1
2result = text.replace(/at/g, "ond");
alert(result); //"cond, bond, sond, fond"第二个参数为字符串,还可以使用一些特殊的字符序列
1
2result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)$n,匹配的第n个捕获组
第二个参数,为函数。
split()
第一个参数,分隔符字符串或RegExp。第二个参数,结果数组的大小。1
2
3
4var 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()
比较两个字符串在字母表中的位置。1
2
3
4var stringValue = "yellow";
alert(stringValue.localeCompare("brick")); //1
alert(stringValue.localeCompare("yellow")); //0
alert(stringValue.localeCompare("zoo")); //-1
fromCharCode()
接受一或多个字符编码。转换成一个字符串。
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
单体内置类型
Global对象
URI编码
encodeURI(),不会对本属于URI的特殊字符进行编码,如冒号,正斜杠问号和井号。
encodeURIComponet(),对任何非标准字符进行编码。1
2
3
4
5
6
7var uri = "http://www.wrox.com/illegal value.htm#start";
//"http://www.wrox.com/illegal%20value.htm#start"
alert(encodeURI(uri));
//"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start"
alert(encodeURIComponent(uri));
URI解码
decodeURI(),只对非URI特殊字符解码。
decodeURIComponet(),解码所以非标准字符。1
2
3
4
5
6
7var uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start";
//http%3A%2F%2Fwww.wrox.com%2Fillegal value.htm%23start
alert(decodeURI(uri));
//http://www.wrox.com/illegal value.htm#start
alert(decodeURIComponent(uri));
eval()
像是一个完整的ECMAScript解析器,只接受一个参数。传入的参数当做实际的ECMAScript语句来解析,然后把执行结果插入到原位置。通过eval()执行的代码,被认为是包含调用的执行环境的一部分,因此被执行代码具有该执行环境相同的作用域链。但在eval()中创建的任何变量或函数都不会被提升。
eval("var msg='hello world';");
alert(msg); //严格模式中会导致错误
Global对象的属性
window对象
Web浏览器都是将Global对象作为window对象的一部分加以实现的。因此全局作用域中声明的所有变量和函数都成为了window对象的属性。
Math对象
min()和max()
1 | var max = Math.max(3, 54, 32, 16); |
舍入方法
1 | alert(Math.ceil(25.1)); //26 |
random()
1 | function selectFrom(lowerValue, upperValue) { |
对象
理解对象
属性类型
ECMAScript中属性分两种:数据属性和访问器属性。
“特性”描述了属性的各种特征,只为了实现JS引擎,不能直接访问。
数据属性
1 | var person = {}; |
一旦把属性定义为不可配置的,就不能再把它便回可配置了。再调用Object.defineProperty()修改除writable之外的特性,都将导致错误。
访问器属性
不包含数据值,包含一对getter和setter函数。都不必需,可创建只读,只写属性。
1 | var book = { |
定义/修改多个属性
1 | var book = {}; |
读取属性的特性
1 | var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); |
创建对象
构造函数模式
1 | function Person(name, age, job){ |
new 操作符,执行步骤:
1,创建1个新对象。2,将构造函数的作用域赋给新对象,this就指向了这个新对象
3,执行构造函数中的代码。4,返回新对象
如果直接调用构造函数,则和普通函数一样。
Person("Greg", 27, "Doctor"); //adds to window
window.sayName(); //"Greg"。通过this,赋值给了window对象。
原型模式
理解原型对象
每一个函数都有一个prototype属性,是一个指针,指向通过构造函数而创建的那个对象实例的原型对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。通过原型,可以把需要定义在构造函数中的实例信息,转移到原型对象中。
1 | function Person(){ } |
确定对象间是否存在原型关系
alert(Person.prototype.isPrototypeOf(person1)); //true
取得[[Prototype]]的值
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(person1.constructor.prototype == Person.prototype); //true
屏蔽原型属性
person1.name = "Greg"; alert(person1.name); //"Greg"
读取实例的属性时,先搜索实例本身,然后搜索原型对象。
删除实例属性,解除屏蔽。
delete person1.name;
检测属性是否存在于实例中
alert(person1.hasOwnProperty("name"));
原型与in操作符
in操作符会在通过对象能够访问给定属性时返回ture,无论属性存在于实例或原型中
1 | var person1 = new Person(); |
- 使用for-in循环时,返回的是所有能通过对象访问的,可枚举([[Enumerable]])的属性。其中既包括实例中的属性和原型中的属性。屏蔽原型中不可枚举属性得实例属性也会在for-in循环中返回。
Object.keys()。接收一个对象,返回一个只包含在此实例中的所有可枚举属性的字符串数组。
1
2
3
4
5var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p=new Person();
var keys = Object.keys(p);
alert(keys); //""Object.getOwnPropertyNames()。接收一个对象,返回一个只包含在此实例中的无论是否可枚举的属性的字符串数组。
1
2
3
4var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"
var keys = Object.getOwnPropertyNames(p);
alert(keys); //""
更简单的原型语法
1 | function Person(){ } |
重写了prototype对象。因此constructor属性变成了Object构造函数,不在指向Person函数1
2
3
4
5var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false。实际访问的是原型对象的constructor属性
alert(friend.constructor == Object); //true
重设constructor属性
Person.prototype = {
constructor: Person,
...
};
缺点:导致constructor属性的枚举特性为ture。改良如下:
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
});
原型的动态性
因为实例和原型之间的连接是一个指针,而非一个副本。创建实例后,对原型对象做的任何修改都能立即从实例上反应出来。
如果先创建实例,又重写了其原型对象,则切断了新原型对象与实例的关系。实例指向的仍是原来的原型对象。
原生对象的原型
1 | alert(typeof Array.prototype.sort); //"function" |
组合构造函数模式和原型模式
组合两种模式,创建自定义类型。构造函数用于定义实例不同的属性,原型模式用于定义方法和共享的属性。
继承
原型链
确定原型和实例之间的关系
1 | instance instanceof Object; //true |
原型链的问题
1 | function SuperType(){ |
组合继承
1 | function SuperType(name){ //父类型 私有属性 |
优点:重用了父类型的方法,避免二次定义与父类型相同的属性
缺点:实例了两次相同的属性,子实例覆盖了父实例创建的私有属性
原型式继承
1 | function object(o){ |
ECMAScript 5 规范了原型式继承1
2
3
4
5var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
寄生组合式继承
1 | function inheritPrototype(subType, superType){ |
函数表达式
闭包
一个函数,有权访问其他函数作用域变量对象的函数。
- 创建时,创建一个预先包含全局变量对象及外层变量对象的作用域链(根据函数是否引用外层函数的变量来决定是否加入外部函数的变量对象,未引用则外部函数的变量对象销毁),保存在内部的[[ Scope ]]属性中。
- 调用时,为此函数创建一个执行环境。
- 复制函数的[[ Scope ]]属性中的对象(指针)构建执行环境的作用域链。
- 创建次函数的活动对象(在此作为变量对象)并被推入执行环境作用域链的前端。
作用域链本质上是一个指向变量对象的指针列表。
闭包与变量
1 | function createFunctions(){ |
关于this对象
this对象在运行时基于函数的执行环境绑定的。当函数作为某个对象的方法被调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。1
2
3
4
5
6
7
8
9
10
11
12
13
14var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
① return this.name;
③ var that=this;
② return function() {
return this.name;
};
}
};
alert(object.getNameFunc()());
- 作为对象的方法调用,this等于此对象。”My Obeject”
- 匿名函数,此时this表示window。”The Window” 。(内部函数搜索this和arguments变量时,只会搜索到其活动对象位置,不可能直接访问到外部函数中的这连个变量。)
- 把外部作用域中的this对象保存在一个闭包能够访问 到的变量,就可以让闭包访问此对象了。
特殊情况,1
2
3alert(object.getName()); //"My Object"
alert((object.getName)()); //"My Object"
alert((object.getName = object.getName)()); //"The Window" 非严格模式
第三行的赋值表达式等于函数本身,this没有得到保持,等于window对象。
闭包导致的内存泄漏
IE9之前的版本对JS对象和COM对象使用不同的垃圾收集例程。BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。
1 | function assign() { |
循环引用,导致内存泄漏
解决方案1
2
3
4
5
6
7
8function assign(){
var ele=document.getElementById('Id');
var id=ele.id;
ele.onclick=function(){
alert(id);
}
ele=null;
}
模仿块级作用域
JS没有块级作用域的概念,在for或if中定义的变量,实际上是在包含函数中而非语句中创建的。
1 | function outputNumbers(count){ |
私有变量
1 | function Person(){ |
静态私有变量
1 | (function(){ |
模块模式
1 | var application = function(){ |
增强模块模式
1 | var application = function(){ |
BOM
window对象
全局作用域
window对象扮演着Global对象的角色。定义在全局作用域中的变量都会成为window对象的属性
区别:
1 | var age = 29; //[[Configurable]]特性为false |
窗口关系及框架
如果页面中包含框架,则每个框架都拥有自己的window对象,并且保存在frames集合中(通过从0开始的数值索引或者框架名称来访问对应的window对象)。
top.frames[0] //top始终指向最高层的框架,也就是浏览器窗口
parent对象 //当前对象直接上层框架,如果没有框架的话。parent=top=window
窗口位置
浏览器在屏幕左边和上边的位置。1
2var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
窗口大小
页面大小,不包括工具栏等浏览器本身。1
2
3
4
5
6
7
8
9
10
11
12var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
if (document.compatMode == "CSS1Compat"){ //是否是标准模式
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else {
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
间歇调用和超时调用
JS是单线程语言,一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JS任务队列,这些任务会按照它们添加到队列的顺序执行。setTimeout()的第二个参数告诉JS再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行。否则它就要等前面的代码执行完了之后再执行。
系统对话框
window.print() //调用打印窗口
window.find('xx') //查找页面是否包含'字符串',返回ture/false
location对象
位置操作
1 | location.hash = "sec1"; // http://..../#sec1 不刷新页面,跳转到页内锚记 |
navigator对象
浏览器及客户端系统相关信息
screen对象
表示客户端的能力,包括浏览器窗口外部的显示器信息,如像素宽度和高度等。
history对象
因history也是window对象的属性,因此每个浏览器窗口,每个标签页乃至每个框架,都有自己的history对象与特定的window对象关联。
1 | history.go(-1) |
DOM
节点层次
Node类型
每个节点都有一个childNodes属性,保存着NodeList对象。NodeList是一种类数组对象,动态实时更新,使用时最好先缓存起来,可以通过索引[i]或者item(i)访问,也有length属性,但并不是Array的实例。
可以通过
var arr=Array.protptype.slice.call(some.childNodes,0);
来进行转换成数组。
Node之间的关系
操作节点
1 | var retNode=someNode.appendChild(someNode.firstChild); //成为第二个节点 |
Document类型
包括HTML元素,HTML声明等。1
2
3
4
5var html=document.documentElement; //HTML元素
html==document.childNodes[0]; //true
html==document.firstChild; //true
var body=document.body; //body的引用
var doctype=document.doctype; //<!DOCTYPE>的引用
在jQuery的$(function(){ … });页面加载事件里,this对象引用的即为Document对象。且直接通过var 声明的变量不会成为Document对象的属性(与全局作用域不同,如全局声明var变量,此变量即成为window对象的属性)。
文档信息
1 | document.title='new title'; |
特殊集合
1 | document.anchores,带name的a元素 |
一致性检测
document.implementation对象,DOM1级只为此对象规定了一个方法,hasFeature()。传入两个参数,要检测的DOM功能名称和版本号。
var hasXmlDom=document.implementation.hasFeature('XML','1.0');
Element类型
特征1
2
3
4nodeType //1
nodeName/tagName //标签名
nodeValue //null
parentNode
HTML元素
HTMLElement,所有HTML元素都是HTMLELment活它的子类型表示。
继承了Element的属性,新增了部分属性:
id,title,lang,dir(语言方向,类似text-align),className(所有class)。
操作特性
1 | var div=document.getElementById('my'); |
直接通过属性名访问和getAttribute()方法得到的结果不一样的两种特性:
style,get得到的CSS文本,属性访问得到对象,用于访问元素样式。
事件,onclick等,get访问代码文本,属性访问得到function对象。
attributes属性
包含一个NamedNodeMap“动态”集合,拥有下列方法:1
2
3
4getNamedItem(name) //返回指定nodeName(tagName)的节点
removedNamedItem(name) //从列表移除指定nodeName
setNamedItem(node) //向列表添加节点
item(id) //返回位于数字id处的节点
取值
ele.attributes['age'].nodeValue
创建元素
1 | var div = document.createElement("div"); |
动态创建的元素特点:不能通过表单reset,与已有同name单选框无关系。
元素的子节点
ele.childNodes
除了IE,其他浏览器会把各子元素之间的回车等空白也计算为一个节点。所以需要遍历子元素时,需要判断元素类型。1
2
3if(chiEle.nodeType==1){
//do some
}
Text类型
特征1
2
3
4
5
6nodeType //3
nodeName //#text
nodeValue //节点文本
parentNode //Element
没有子节点
length属性
方法1
2
3appendData(txt),deleteData(offset,count),
insertData(offset,count,text),replaceData(offset,count,text),
splitText(offset),substringData(offset,count)
创建
document.createTextNode('<strong>Hello</strong>');
Comment类型
特征1
2
3
4
5nodeType //8
nodeName //'#comment'
nodeValue //注释的内容
parentNode //Document或Element
无子节点
CDATASection类型
特征1
2
3
4
5
6只针对XML文档,CDATA区域。与Comment类似,继承自Text类型
nodeType //4
nodeName //#cdata-section
nodeValue //CDATA区域的内容
parentNode //Document或Element
无子节点
DocumentType类型
很少有浏览器支持。表示文档类型,对应<!DOCTYPE>等。
Attr类型
元素的特性
DOM操作技术
动态脚本
页面加载时不存在,将来通过修改DOM动态添加的脚本。
外部引用
1
2
3
4
5
6
7function addScript(){
var script = document.createElement("script");
script.type = "text/javascript";
script.text = "function sayHi(){alert('hi');}";
document.body.appendChild(script);
sayHi();
}行内方式
1
2
3
4
5
6
7
8
9
10function loadScriptString(code){
var script = document.createElement("script");
script.type = "text/javascript";
try {
script.appendChild(document.createTextNode(code));
} catch (ex){
script.text = code; //IE特定,不支持对Script添加子节点
}
document.body.appendChild(script);
}
动态样式
外部引用
1
2
3
4
5
6
7function addStyle(){
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode("body{background-color:red}")); //error in IE
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}嵌入方式
1
2
3
4
5
6
7
8
9
10
11function loadStyleString(css){
var style = document.createElement("style");
style.type = "text/css";
try{
style.appendChild(document.createTextNode(css));
} catch (ex){
style.styleSheet.cssText = css; //IE
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
选择符API
使用CSS选择符查询匹配的DOM元素。
querySelector()
接受一个CSS选择符,返回与该模式匹配的第一个元素,没找到则返回null。
1 | var body = document.querySelector("body"); |
querySelectorAll()
返回所有匹配的元素,一个NodeList实例(此方法返回的实际上是一组元素的快照,避免不断动态查询带来的性能影响)。没有匹配则返回一个空的NodeList。1
2
3
4var ems = document.getElementById("myDiv").querySelectorAll("em");
var selecteds = document.querySelectorAll(".selected");
var strongs = document.querySelectorAll("p strong");
var s1=strongs[0];
matchesSelector()
如果调用元素与该选择符匹配,返回ture,否则返回false。1
2
3if (matchesSelector(document.body, "body.page1")){
alert("It's page 1!");
}
支持不完善,需要做跨浏览器兼容。1
2
3
4
5
6
7
8
9
10
11
12
13function matchesSelector(element, selector){
if (element.matchesSelector){
return element.matchesSelector(selector);
} else if (element.msMatchesSelector){
return element.msMatchesSelector(selector);
} else if (element.mozMatchesSelector){
return element.mozMatchesSelector(selector);
} else if (element.webkitMatchesSelector){
return element.webkitMatchesSelector(selector);
} else {
throw new Error("Not supported.");
}
}
元素遍历
对于元素见得空格,IE9及之前版本不会返回文本节点,其他浏览器都会返回文本节点。
子节点查询的元素版:
childElementCount属性,子元素(不含文本和注释节点啊)个数。
firstElementChild,lastElementChild,previousElementSibling,nextElementSibling
children属性,HTMLCollection实例,只包含元素子节点。childNodes的元素版。
跨浏览器兼容1
2
3
4
5
6
7
8
9
10
11
12if (document.body.firstElementChild){
var i,
len,
child = document.body.firstElementChild;
while(child != document.body.lastElementChild){
document.write("<p>" + child.tagName + "</p>");
child = child.nextElementSibling;
}
} else {
document.write("<p>Element Traversal API not supported.</p>");
}
HTML5
document.head,head部分引用
document.charset,字符集
类的扩充
getElementsByClassName()
所有元素都可调用1
2
3
4
5classList //DOMTokenList
add(value) //添加或忽略
contains(value) //是否包含
remove(value) //删除
toggle(value)
滚动
scrollIntoView()
任何元素可调用,出现在视口中。
传入true或不传,顶部与视口对齐。
传入false,底部对齐。
DOM2和DOM3变化
Node类型
localName,不带命名空间前缀的节点名称
namespaceURI,不带命名空间前缀的节点名称
prefix,命名空间或者null
Document类型
新增了带NS后缀的方法,表示特定命名空间下的操作,比如:
createElementNS(ns,tagName):创建属于ns命名空间的元素
createAttributeNS(ns,attr):使用给定attr创建一个ns命名空间的新特性
getElementsByTagNameNS(ns,tagName):返回特定命名空间的元素NodeList
元素大小
偏移量
某个元素在页面上的偏移量计算:1
2
3
4
5
6
7
8
9
10function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
客户区大小
元素内部空间大小,因此滚动条占用的空间不计算。
确定浏览器视口大小:
document.documentElement.clientWidth //document.body代表body,document.documentElement //代表html标签
滚动大小
scrollHeight,没有滚动条的情况下,元素总高度
scrollWidth,同上,元素内容总宽度
scrollLeft,被隐藏在内容区域左侧的像素数。设置可以改变元素滚动位置
scrollTop,上方的元素,设置可以改变元素滚动位置
(需要设置overflow属性才能滚动)
确定文档的总高度时,必须获得scrollHeight/clientHeight中的最大值,才能跨浏览器得到准确的结果。
Math.max(ducment.documentElement.scrollHeight,document,documentElement.clientHeight)
遍历
TreeWalker
1 | function makeList() { |
NodeFilter.FILTER_SKIP,跳过节点前进道子树种的下一个节点
NodeFilter.FILTER_REJECT,跳过节点及该节点整个子树。
walker可以眼任何方向移动
walker.firstChild();
walker.nextSibling();
walker.currentNode,当前节点。
范围
事件
事件流
事件处理程序需
DOM0级事件处理程序
1 | bar btn=document.getElementById('my'); |
DOM0级方法指定的事件处理程序被认为是元素的方法,在元素的作用域中运行,this引用当前元素。
DOM2级事件处理程序
1 | var btn = document.getElementById("myBtn"); |
false,冒泡阶段调用,true,捕获阶段调用。
作用域同DOM0级方法一样。好处是,可以添加多个事件处理程序,且按添加顺序执行。
通过此方法添加的事件只能使用removeEventListener()移除,传入的参数与添加时使用的参数相同(同一个函数对象),所以通过addEventListener()添加的匿名函数将无法移除。
事件对象
所有事件都会有的成员
1 | document.body.onclick = function(event){ |
事件类型
UI事件
在window上发生的任何事件都可以在body元素上通过相应的特性来指定,因为HTML无法访问window元素,为了向后兼容
例:window的load事件,
unload事件
兼容性很不好,chrome不会显示文本,IE显示乱码1
2
3
4
5EventUtil.addHandler(window, "beforeunload", function(event) {
var msg = "will you leave?";
event.returnValue = msg;
return msg;
});
scroll事件
document.body.scrollTop获取滚动高度。
火狐使用document.documentElement.scrollTop。
焦点事件
鼠标与滚轮事件
客户区(可视)坐标
event.clientX和event.clientY
页面坐标
event.pageX,event.pageY
屏幕坐标
event.screenX,event.screenY
修改键
e.shiftKey,e.ctrlKey,e.altKey,e.metaKey。布尔值,代表是否按下了相应按键,支持同时按。
关联元素
e.relatedTarget,只对mouseover和mouseout事件才包含值,代表与target发生关系的元素。
鼠标按钮
e.button,按下或释放的按钮。0,主按钮,1,滚轮,2,次按钮。
滚轮事件
mousewheel,任何元素上出发,最终会冒泡到document或window对象。event对象包含一个特殊的wheelDelta属性,向前滚动wheelDelta是120的倍数,向后滚动wheelDelta是-120的倍数。
触摸设备
键盘与文本事件
keydown,按下键盘上任意键时触发,按住不放会重复触发。
keypress,按下奸商上字符键时触发,按住不放会重复触发。
keyup,释放键盘上的键时触发keydown->keypress->keyup。
支持4个修改键。
键码,event.keyCode,表示触发keydown或keyup事件特定的键,如回车-13,空格-32。
字符编码,event.key,事件产生的相应文本字符或键名。
文本事件
textInput。按下能够输入实际字符的键时才会被触发,而keypress事件则在按下那些能够影响文本显示的键时也会触发。
event.data,实际输入的字符文本。
event.inputMethod,代表文本是如何输入到控件中的,从而验证有效性(只有IE支持)。
HTML5事件
contextmenu事件
windows中,右键单击。Mac,Ctrl+单击。
冒泡,可以为document指定一个事件处理程序自定义右键菜单。
1 | document.oncontextmenu=function(event){ |
beforeUnload事件
必须添加给window对象。firefox和opera不支持。chrome不显示message。1
2
3
4
5
6EventUtil.addHandler(window, "beforeunload", function(event){
event = EventUtil.getEvent(event);
var message = "I'm really going to miss you if you go.";
event.returnValue = message;
return message;
});
DOMContentLoaded事件
DOM树加载完毕之后触发,不理会图片,js,css等。
pageShow,pageHide事件
必须添加给window对象。pageShow在load后触发,pageHide在unload之前触发。兼容性不好。
hashchange事件
必须添加给window对象。url发生变化时触发,包含oldURL和newURL两个属性。
设备事件
只在移动设备上实现。
orientationchange事件
横向纵向查看模式。只有Safari支持
deviceorientation事件
1 | EventUtil.addHandler(window, "deviceorientation", function(event){ |
devicemotion事件
1 | EventUtil.addHandler(window, "devicemotion", function(event){ |
触摸和手势事件
触摸事件
都会冒泡。
Touch对象包含:
identifier,唯一ID,clientX/Y,pageX/Y,screenX/Y,target,触摸的DOM节点。
1 | function handleTouchEvent(event){ |
模拟事件
模拟鼠标事件
1 | var event = document.createEvent("MouseEvents"); |
模拟键盘事件
1 | EventUtil.addHandler(btn, "click", function(event){ |
表单脚本
选择框脚本:
HTMLOptionElement对象,属性有:index,label,selected,text,value。
取得选择的option,select.options[select.selectedIndex];
选择,select.option[0].selected = true; 对其他option无影响。
select.selectedIndex = 0,影响多选框其他option。
JSON
JSON序列化:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20var bookCopy = JSON.parse(jsonText, function(key, value){
if (key == "releaseDate"){
return undefined;
} else {
return value;
}
});
var jsonText = JSON.stringify(book, ["title", "edition"], 4); //只序列化字段,格式化缩进个数
var jsonText = JSON.stringify(book, null, "--"); //缩进占位符
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011,
toJSON: function(){
return this.title;
}
};
Ajax
XHR默认头部信息
发送请求
通过setRequestHeader()设置自定义头部,jQuery方式:1
2
3
4
5
6
7
8
9
10$.ajax({
type: "GET",
url: "...",
beforeSend: function(request) {
request.setRequestHeader("Test", "content");
},
success: function(result) {
alert(result);
}
});
XHR2级
FormData
1 | var data = new FormData(); |
进度事件
1 | var xhr = createXHR(); |
跨域
XHR对象,默认只能访问与它同一个域中的资源。
CORS:发送请求时,需要添加一个额外的Origin头部,Origin:http://www.xxx.com。服务器如果任何请求,就在Access-Control-Allow-Origin头部中挥发相同的源(如果不需要跨域保护,可会回发 *),例如Access-Control-Allow-Origin:http://www.xxx.com
大部分浏览器默认实现CORS的原声支持,访问时,使用绝对URL即可。但跨域XHR对象也有一些限制:不能自定义头部,不能发送和接受cookie,获取自定义头部总会返回空
其他跨域技术
图像Ping,JSONP,Comet,Web sockets。
WebSockets
在一个单独的持久链接上提供全双工、双向通信。第一次取得服务器响应后,建立的链接会从HTTP协议升级成Web Socket协议(需要服务器支持)。
Web Sockets协议:’ws://‘和’wss://‘(加密)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var socket = new WebSocket('ws://www.xxx.com/handler'); // 不受同源策略影响,可以跨域
// readState 属性:WebSocket.CONNECTING(0) / OPEN(1) / CLOSING(3) / CLOSED(2)
socket.send('xxx'); //只能发送纯文本,发送复杂数据结构,需要先序列化
socket.onmessage = function(event){ // 获取服务器消息
var data=event.data;
}
socket.close() //关闭链接 ,状态由2到3
WebSocket事件:
不支持DOM2级事件侦听器,必须使用DOM0级语法定义事件。
socket.onopen = function(){};
socket.onerror = function(){};
socket.onclose = function(e){
// e.wasClean 是否明确关闭
// e.code 服务器返回码
// e.reason 服务器发回的消息
};
高级技巧
高级函数
安全的类型检测
检测是否为数组/函数/正则表达式:
Object.prototype.toString.call(obj) == '[object Array]'/'[object Function]'/'[object RegExp]';
作用域安全的构造函数
假设一个构造函数没有搭配new 关键词而调用,那么this默认绑定到window对象,则可能导致意外的错误。1
2
3
4
5
6
7
8
9function Person(name, age, job){
if (this instanceof Person){ //判断是否为指定类型的对象,再进行操作
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name, age, job);
}
}
函数绑定
绑定后返回一个函数,无论再对此函数调用什么apply或call改变this的引用,都不会改变执行结果
handleClick.bind(obj)。1
2
3
4
5
6function bind(context){
var fn=this;
return function(){
return fn.apply(context, arguments);
};
}
柯里化
使用闭包创建一个已经设置了部分参数的函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function curry(fn){
var args = Array.prototype.slice.call(arguments, 1);
return function(){
var innerArgs = Array.prototype.slice.call(arguments),
finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}
function add(num1, num2){
return num1 + num2;
}
var curriedAdd = curry(add, 5);
alert(curriedAdd(3)); //8
bind的柯里化实现1
2
3
4
5
6
7
8
9var handler = {
message: "Event handled",
handleClick: function(name, event){
alert(this.message + ":" + name + ":" + event.type);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn")); //第一个参数是绑定的this,第二个参数开始是预设置的参数。
防篡改对象
不可扩展
1 | var person = { name: "Nicholas" }; |
静默失败undefined,严格模式抛出异常
密封
不可扩展,[[Configurable]]特性被设置为false,不能删除属性和方法。但是属性值是可以修改的。1
2
3
4
5
6
7var person = { name: "Nicholas" };
Object.seal(person);
alert(Object.isSealed(person)); //true
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas"
冻结
最严格。及不可扩展,又是密封的,对象的[[Writable]]特性被设置为false,如果定义了[[Set]],则访问器属性仍是可写的。1
2
3
4
5
6
7
8
9
10
11var person = { name: "Nicholas" };
Object.freeze(person);
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
alert(Object.isExtensible(person)); //false
alert(Object.isSealed(person)); //true
alert(Object.isFrozen(person)); //true
分隔执行过程
当执行数据量过大,为避免造成JS线程一直在进行中,导致UI线程被卡住。推荐对数据分割处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
function chunk(array, process, context){ // context可选的
setTimeout(function(){
var item = array.shift();
process.call(context, item);
if (array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100); // 100毫秒 给线程一个协调的时间
}
function printValue(item){
var div = document.getElementById("myDiv");
div.innerHTML += item + "<br>";
}
chunk(data.concat(), printValue); // 不想改变原数组的数据,可以使用concat或slice创建一个副本
函数节流
一些会被反复执行的事件,如果频率太高容易导致浏览器挂起或崩溃。需要对该函数进行节流
适用于resize事件或over事件等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function throttle(method, scope) { // 可选的scope
return function() {
clearTimeout(method.tId);
method.tId = setTimeout(function() {
method.call(scope);
}, 100);
}
}
function resizeDiv() {
var div = document.getElementById("myDiv");
div.style.height = div.offsetWidth + "px";
}
window.onresize = throttle(resizeDiv);
自定义拖动功能
1 | function DrapDrop() { |
离线应用和客户端存储
离线应用
离线检测
navigator.onLine
离线事件
1 | EventUtil.addHandler(window, "online", function(){ |
数据存储
cookie
限制:存在客户端计算机上,每个域最多几十个。只能保存单一文本,总共最大尺寸4k。
属性:
key,名称,必须经过URL编码
value:值,必须经过URL编码
域:在某些域下才有效并携带发送到服务器。(.baidu.com对baidu.com的所有子域都有效)
路径:指定路径有效并携带。(http://www.baidu.com/news/,跟路径则不会发送cookie)
失效时间:时间戳,什么时候删除cookie。
安全标志:secure(非名值对儿),只有在SSL链接下才发送。
Set-Cookie: name=value; domain= .wrox.com; path=/; secure
Http专有cookie
可以从浏览器和服务器设置,但是只能从服务器端读取,JS无法获取。
Web Storage
Storage类型
clear():清空所有值
getItem(name):获取name的值
key(index):获取index处值的名称
removeItem(name):删除
setItem(name,value):设置
PS:只能存储字符串
sessionStorage对象
会话存储,存储某个特定会话的数据,保持到浏览器关闭。sessionStorage中的数据只能由最初设置的页面访问,不能跨页面
localStorage对象
限制:同一来源有效,即同一个域(子域名无效),同一种协议,同一个端口。每个来源2~5MB大小限制。
有效期:一直存在,直到JS删除或用户清楚缓存
IndexedDB
替代了Web SQL Database API。保存结构化数据的一种数据库。操作完全是异步进行,因此,大多数操作会以请求的方式进行,异步返回成功结果或失败错误。
限制:不能跨域,必须同源(域,协议,端口),大小5MB左右。
获取:(因为浏览器厂商提供的API都有前缀)
var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
1 | (function() { |
最佳实践
性能优化
避免不必要的属性查找
访问数组元素是一个O(1)的操作,访问对象的属性则是O(n)的操作。避免多次查找统一属性,可以先存储起来。
优化循环
- 减值迭代,从最大值到0,更高效。
- 简化终止条件,避免属性查找或O(n)的操作。比如:i<arr.length。
- 简化循环体。
- 使用do-while,避免最初的终止条件计算。
Duff装置
仅在处理大数据集时使用,更加高效。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var arr = []
var iterations = Math.floor(arr.length / 8);
var leftover = arr.length % 8;
var i = 0;
if (leftover > 0) {
do {
process(arr[i++]);
} while (--leftover > 0);
}
do {
process(arr[i++]); //循环再开,比单纯的循环调用更加高效
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
} while (--iterations > 0);
最小化语句数
多个声明合一,更高效:
var count=5,
color='red',
arr=[1,2,3];
初始化数组和对象时使用字面量
var arr=[1,2,3];
var obj={age:20};
优化DOM交互
- 最小化更新DOM次数。
- 使用innerHTML,一次性更新。
- 使用事件代理,利用事件冒泡,在祖先节点上处理事件。减少事件绑定数量。
- 减少使用HTMLCollection对象
getElementByxxx(),childNodes,attributes,document.forms,document.images等都会返回HTMLCollection,每次访问它的属性时都会在文档上进行实时的查询,开销昂贵。
其他
- 原生方法更快
- switch语句更快
- 位运算符更快
新兴的API
这些API都不在H5的规范中,只是与H5相关。
Page Visibility API
探测页面是否最小化或者隐藏在其他标签后面。方便资源的暂停。
var isHide = document.hidden || document.msHidden || document.webkitHidden;
document.visibilityState:4种表示不同状态的值
visibilitychange事件
EventUtil.addHandler(document, "msvisibilitychange", handleVisibilityChange);
EventUtil.addHandler(document, "webkitvisibilitychange", handleVisibilityChange);
Geolocation API
获得用户当前地理位置,需要用户授权。1
2
3
4
5
6
7
8
9
10
11
12
13navigator.geolocation.getCurrentPosition(function(position){
// position.coords对象有以下属性:latitude 维度,longitude 经度,accuracy 单位
}, function(error){
// error.message
});
var id = navigator.geolocation.watchPosition(function(position) {
// 方法第一次会取得当前位置,然后在系统发生位置改变时被调用
}, function(error) {
//
});
clearWatch(id); //取消追踪
File API
FileReader
File类型,包含name,size,type属性。event.files1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = EventUtil.getTarget(event).files,
type = "default",
reader = new FileReader();
if (/image/.test(files[0].type)){
reader.readAsDataURL(files[0]);
type = "image";
} else {
reader.readAsText(files[0]);
type = "text";
}
reader.onerror = function(){
output.innerHTML = "Error code :" + reader.error.code;
//1,未找到。2,安全错误。3,读取中断。4,文件不可读。5,编码错误
};
reader.onprogress = function(event){
if (event.lengthComputable){
progress.innerHTML = event.loaded + "/" + event.total;
}
};
reader.onload = function(){
var html = "";
switch(type){
case "image":
html = "<img src=\"" + reader.result + "\">";
break;
case "text":
html = reader.result;
break;
}
output.innerHTML = html;
};
});
部分读取
返回blob类型(file的父类型)1
2
3
4
5
6
7
8
9
10
11
12
13function blobSlice(blob, startByte, length){
if (blob.slice){
return blob.slice(startByte, length);
} else if (blob.webkitSlice){
return blob.webkitSlice(startByte, length);
} else if (blob.mozSlice){
return blob.mozSlice(startByte, length);
} else {
return null;
}
}
var blob = blobSlice(files[0], 0, 32);
对象URL
bloblURL,指向一块内存地址1
2
3
4
5
6
7
8
9
10
11function createObjectURL(blob){
if (window.URL){
return window.URL.createObjectURL(blob);
} else if (window.webkitURL){
return window.webkitURL.createObjectURL(blob);
} else {
return null;
}
}
var url = createObjectURL(files[0]);
output.innerHTML = "<img src=\"" + url + "\">";
文件拖放并上传
1 | var droptarget = document.getElementById("droptarget"); |
Web workers
使用worker
使用后台线程/进程执行复杂的运行实现异步。如加/解密,图片处理等
1 | var data = [23,4,7,9,2,14,6,651,87,41,7798,24], |
worker中1
2
3
4
5
6
7
8
9
10
11
12
13self.onmessage = function(event){
var data = event.data;
data.sort(function(a, b){
return a - b;
});
self.postMessage(data);
};
self.onerror = function(event){
// event.filename
// event.lineno
// event.message
}
self.close(); // 停止工作
全局方法
importScripts("com1.js","com2.js"); // 异步加载。按顺序执行。
worker全局作用域
worker单独一个作用域,不能访问DOM元素和标签。
this引用的是worker对象。
限制版本的navigator对象。
制度的location对象。
setTimeout()、setInterval()、clearTimeout()、clearInterval()方法
XMLHttpRequest函数。
工具
链接 | 描述 |
---|---|
client.js | 浏览器及版本检测工具 |
EventUtil.js | 跨浏览器事件处理工具 |
CookieUtil.js | cookie操作工具 |
EventTarget.js | 自定义事件操作工具 |
richtext.html | 自制富文本编辑器 |