基本概念
- 基本数据类型包括Undefined/Null/Boolean/Number和String
- 无须指定函数的返回值,实际上,未指定返回值的函数返回的是一个特殊的undefined值
变量、作用域和内存问题
- 基本类型值在内存中占据固定大小的空间,因此保存在栈内存中
- 引用类型的值是对象,保存在堆内存中
- 确定一个值是哪种基本类型用typeof,确定一个值是哪种引用用instanceof
- 所有变量都存在于一个执行环境当中,这个执行环境决定了变量的生命周期,以及如一部分代码可以访问其中的变量。执行环境有全局执行环境和函数执行环境,每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用链。
- Javascript是一门具有自动垃圾收集机制的编程语言,离开作用域的值被自动标记为可以回收,“标记清除”是目前主流的垃圾收集算法,另一种是“引用记数”,这种算法的思想是跟踪记录所有值被引用的次数。在IE中访问非原生Javascript对象(DOM元素)时,这种算法会因为循环引用导致引用计数永远无法清零
Array类型
- concat()先创建当前数组一个副本,然后接收的参数添加到副本末尾,最后返回
- slice()基于当前数组中的一个或多个项创建一个新数组
- splice()数组的增删
- indexOf()/lastIndexOf()查找项和查找起点位置,比较用的===。支持IE9+
- every()=and
- filter()返回函数返回为true的项数组
- forEach()运行指定函数,无返回
- map()返回每次函数调用的结果组成的数组
- some()=||
- reduce()/reduceRight(),function(prev,cur,index,array),IE9+
Date类型
- 1
- 2
- 3
- 1
- 2
- 3
RegExp类型
- RegExp类型中需要转义的元字符包括([{\^$|)?*+=.]}
- 1
- 2
- 1
- 2
RegExp构造函数的两个参数都是字符串,在某些情况下要对字符进行双重转义
字面量模式 | 等价的字符串 |
---|---|
/\ [bc\]at/ | “\\[bc\\]at” |
/\w\\hello\\123/ | “\\w\\\\\hello\\\\123” |
- RegExp实例属性:global是否设置了g标志,ignoreCase是否设置了i标志,lastIndex表示开始搜索下一个匹配项的字符位置,multiline是否设置m 标志,source正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。正则表达式字面量始终会共享同一个RegExp实例,所以在循环时需要注意,会从上一次搜索位置开始匹配。
- exec()专门为捕获组而设计,没有匹配返回null,返回数组包含两个额外属性:index和input。其中index表示匹配项在字符串中的位置,input表示应用正则表达式的字符串。数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果没有则数组只有一项)。
- test()匹配返回true否则false
Function类型
-函数内部对象arguments和this,arguments除了保存函数参数,还有一个属性callee指向这个arguments对象的函数
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-this引用的是函数据以执行的环境对象——或者也可以说是this值。
-ECMAScript5规范化了另一个函数对象属性:caller。除了Opera早期版本不支持。这个属性中保存调用当前函数的函数的引用,如果在全局作用域中调用,则caller的值为null。
-length表示希望接收的命名参数的个数。
-prototype是保存引用类型所有实例方法的真正所在。诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。该属性不可枚举。
-每个函数都包含两个非继承方法:apply()和call()。作用是在特定的作用域调用函数,实际上等于设置函数体内this对象的值。区别在于传入第二个参数是arguments对象还是具体参数。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
基本包装类型
Boolean、Number、String
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 字符串操作方法
concat()/slice()/substr()/substring()都不会修改字符串本身的值,只是返回一个基本类型的字符串,对原始字符串没有任何影响。
indexOf()/lastIndexOf(),没有找到返回-1
trim()返回字符串副本,IE9+支持
match(正则/RegExp)同exec()
search(正则/RegExp)无匹配返回-1
replace(RegExp/字符串,字符串/函数)
split(RegExp/字符串,length)
charCodeAt()/formCharCode()
单体内置对象
- Global对象
encodeURI()/encodeURIComponent()对URI(Uniform Resource Identifiers,能用资源标识符)进行编码,以便发送给浏览器。区别在于前者不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井号;后者会对所有非标准字符进行编码。替代只能编码ASCII字符的escape() - eval()参数当然实际的ECMAScript语句来解析,然后把执行结果插入到原来位置。在eval()中创建的任何变量和函数都不会被提升。在严格模式下无法访问 eval中创建的变量和函数。
- undefined/NaN/Infinity以及所有原生引用类型的构造函数都是Global的属性
- Math.min()/max()/ceil()/floor()/round()/random()
2-10整数间的随机数,包括2跟10
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
面向对象的程序设计
对象是散列表:无非就是一组名值对,其中值可以是数据或函数。
-
属性类型
- 数据属性
[[Configurable]]能否delete属性从而重新定义属性,能否修改属性描述,默认true
[[Enumerable]]能否通过for-in循环,默认true
[[Writable]]能否修改属性值,默认true
[[Value]]包含这个属性的数据值,默认undefined -
访问属性
同上前2
[[Get]]读取属性时调用的函数,默认undefined
[[Set]]写入属性时调用的函数,默认undefined
Object.defineProperty(),configurable设为false后再调用该方法就会抛出错误了,该方法调用时,如果不指定默认都是false。访问属性不能直接定义,必须使用Object.defineProperty()来定义。- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
不一定要同时指定getter和setter。在非严格模式下会返回undefined或被忽略,在严格模式下会抛出错误。ECMAScript 5又定义了Object.definedProperties()。
IE9+(IE8部分支持)、Firefox 4+、Safari 5+、Opera 12+、Chrome
- 数据属性
创建对象
- 工厂模式
没法解决对象识别的问题,即对象类型问题。 -
构造函数模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 创建一个新对象;
- 将构造函数的作用域赋给新对象;
- 执行构造函数中的代码;
- 返回新对象。
构造函数与其他函数的唯一区别在于调用它们的方式不同。任何函数,只要通过new来调用。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
但此模式还是有缺点就是不同实例上的同名函数是不相等的,然而创建两个完成同样任务的Function实例是没有必要的;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此可以像下面一样解决此问题。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-
原型模式
创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,该对象的用途是包含可以由特定类型的所有实例共享的属性和方法,prototype就是通过调用构造函数而创建的那个对象实例的原型对象。- 1
- 2
- 1
- 2
可以通过对象实例访问保存在原型中的值,但不能重写原型中的值,可以屏蔽不可以重写。想要取回原型中的值用delete person1.name;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
Object.keys和Object.getOwnPropertyNames都可用来替代fon-in循环,IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome支持。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果重写整个原型对象,情况就不一样了。调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。
原型对象的缺点,省略了为构造函数传递初始化参数这一环节还有就是共享所导致,当包含引用类型值的属性时会有共享问题。
- 组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性,同时又支持向构造函数传递参数。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 动态原型模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 寄生构造函数模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
该种模式下返回的对象与构造函数或者与构造函数的原型属性之间没有关系,既构造函数返回的对象与在构造函数外部创建的对象没什么不同,因此不能依赖instanceof操作符来确定对象类型。
- 稳妥构造函数模式
稳妥对象指没有公共属性,而且其方法也不引用this的对象。
javascript
function Person(name, age, job) {
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function() {
alert(name);
};
//返回对象
return o;
}//除了使用sayName()方法外,没有其他办法访问name值(传入到构造函数中的原始数据)
var friend = Person("Nicholas",29,"Software Enginner");
friend.sayName();
稳妥构造函数模式提供的这种安全性适合在某些安全执行环境。与寄生构造函数模式类似,该模式创建的对象与构造函数之间没什么关系。
继承
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
调用instance.getSuperValue()会经历三个搜索步骤:1)搜索实例2)搜索SubType.prototype;3)搜索SuperType.prototype,最后一步才会找到
实现的本质是重写原型对象,代之以一个新类型的实例。原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。
原型链的问题:包含引用类型值属性的原型会导致所有实例共享该属性;在创建子类型的实例时,不能向超类型的构造函数传递参数,所以实践中很少会单独使用原型链。
借用构造函数
在子类型构造函数的内部调用超类型构造函数。解决了原型链的问题。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
虽然如此还是有问题:无法避免方法都在构造函数上定义,就无法函数复用了。在超类的原型中定义的方法对子类而言是不可见的,所以借用构造模式也很少用。
组合模式
原型链和借用构造函数组合在一块,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
- 1
- 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
- 1
- 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
原型式继承
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
借助原型可以基于已有的对象创建新对象,不必因此创建自定义类型。ECMAScript5通过新增Object.create()方法规范化了原型式继承。接收参数:一个用作新对象原型的对象和(可选)一个为新对象定义额外属性的对象。一个参数时等同于上面的object(),在没有必要创建构造函数时使用。
寄生式继承
与寄生构造函数和工厂模式类似,创建一个仅用于封闭继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
寄生组合式继承
组合继承的不足是无论什么情况下都会调用两次超类型构造函数(创建子类型原型的时候和子类型构造函数内部)。寄生组合式继承解决此问题,思路:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。基本模式如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
将前面组合继承中的语句SubType.prototype = new SuperType();替换成inheritPrototype(SubType,SuperType);效率体现在它只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变。
寄生组合式继承是引用类型最理想的继承范式。
总结
- 创建对象
- 工厂模式:使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。
- 构造函数模式:可以创建自定义引用类型,像创建内置对象实例一样使用new操作符。缺点是每个成员都无法得到复用,包括函数。
- 原型模式:使用构造函数的prototype属性来指定那些应该共享的属性和方法。组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。
js主要通过原型链实现继承,通过将一个类型的实例赋值给另一个构造函数的原型来实现,子类型能够访问超类型的所有属性和方法。问题在于对象实例共享所有继承的属性和方法。解决方法是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。使用最多的是继承模式是组合继承,使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性。 - 原型式继承:执行对给定对象的浅复制,复制得到的副本还可以得到进一步改造。
- 寄生式继承:基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类构造函数而导致低效率问题,可以将此模式与组合继承一起使用。
- 寄生组合式继承:集寄生式继承和组合继承的优点与一身。
函数表达式
当某个函数被调用时,会创建一个执行环境及相应的作用域链。
执行环境(execution context)定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。但是有其他办法来延长作用域链。有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。当执行流进入下列两种语句时,作用域链就会得到加长:
try-catch 语句的 catch 块
with 语句
这两个语句都会在作用域链的前端添加一个变量对象。对 with 语句来说,会将指定的对象添加到作用域链中。对 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
在 IE8 及之前版本的 JavaScript 实现中,存在一个与标准不一致的地方,即在 catch 语句中捕获的错误对象会被添加到执行环境的变量对象,而不是 catch 语句的变量对象中。换句话说,即使是在 catch 块的外部也可以访问到错误对象。 IE9 修复了这个问题。
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况有所不同,因为在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中,参考下面的代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
当createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象(可以看成是暂时的全局活动对象)。直到匿名函数被销毁后, createComparisonFunction()的活动对象才会被销毁。
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。因为闭包所保存的是整个变量对象,而不是某个特殊的变量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。匿名函数的执行环境具有全局性,this指向window。普通函数被调用时,其他活动对象都会自动取得两个特殊变量this和arguments。内部函数搜索这两个变量时只会搜索到其活动对象为止。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
模仿块级作用域
- 1
- 2
- 3
- 1
- 2
- 3
无论什么地方,只要临时需要一些变量,就可以使用私有作用域。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
私有变量
不能在函数外部访问的变量叫私有变量。
特权方法:有权访问私有变量(任何函数中定义的变量)和私有函数的公有方法。在函数内部创建一个闭包,闭包通过自己的作用链访问私有变量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
静态私有变量
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
调用publicMethod或新建一个MyObject实例都会赋予privateVariable一个新的值。结果就是所有实例都会返回相同的值。每个实例都没有自己的私有变量。
该模式用于为自定义类型创建私有变量和特权方法的。
模块模式(为单例创建私有变量和特权方法)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这种模式在需要对单例(JavaScript是以对象字面量的方式来创建单例对象的)进行某些初始化,同时又需要维护其私有变量时是非常有用的。
BOM
- 取得页面视口大小
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- window.open(URL, target, string, boolean)返回指向新窗口的引用,创建的窗口允许我们针对窗口调整大小和移动位置、关闭等。opener指向打开它的原始窗口。有很安全限制,如不允许关闭地址栏、默认情况下不允许移动弹出窗口或调整其大小。此外,有的浏览器只根据用户操作来创建弹出窗口。在页面尚未加载完成时调用window.open()的语句根本不会执行,换句说就是只能通过单击或者击键来打开弹出窗口。对于不是用户有意打开的弹出窗口,Chrome采取的不是简单的屏蔽而是只显示它们的标题栏,放在浏览器窗口的右下角。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 超时调用和间歇调用
在使用超时调用时,没必要跟踪超时调用ID,因为每次执行完代码之后,如果不再设置另一次超时调用,调用就会自行停止。一般认为使用超时调用来模拟间歇调用是一种最佳模式。后一个间歇调用可能会在前一个间歇调用结束之前启动。 - location对象
既是window的属性也是document的属性;
属性名 | 例子 | 说明 |
---|---|---|
hash | “#contents” | 散列#号后面的跟的内容,没有则返回空 |
host | “www.wrox.com:80” | |
hostname | “www.wrox.com” | |
href | “http://ww.wrox.com” | |
pathname | “/WileyCDA/” | |
port | “8080” | |
protocol | “http:”或”https” | |
search | “?q=javascript” |
- 1
- 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
- 1
- 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
客户端检测
- 能力检测
在可能的情况下,尽量使用typeof进行能力检测。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 怪癖检测
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 用户代理检测
优先级排在能力检测和怪癖检测之后。确切知道浏览器的名字和版本号不如确切知道它使用的是什么呈现引擎。主要有五大呈现引擎:IE/Gecko/WebKit/KHTML和Opera。
- 1
- 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
- 1
- 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
事件
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段(从外到里)、处于目标阶段和事件冒泡阶段。
事件特性的值是javascript,因此不能在其中使用未经转义的HTML语法字符。
当前元素是表单输入元素,作用域中还会包含访问表单元素的入口
- 1
- 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
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 1
- 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
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 事件对象
所有浏览器都支持event对象,但支持方式不同。
DOM中的事件对象,this始终等于currentTarget,而target则只包含事件的实际目标。
高级技巧
- 安全类型检测
javascript内置的类型检测并非完全可靠。
var isArray = value instanceof Array;代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域中,因为Array是window的属性,如果value是在另外框架中定义的数组,那么代码返回为false。
- 1
- 2
- 3
- 1
- 2
- 3
- 作用域安全构造函数
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 惰性载入函数
惰性载入表示函数执行的分支仅会发生一次。
- 1
- 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
- 1
- 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
- 函数绑定
创建一个函数,可在特定的this环境中以指定参数调用另一个函数。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
支持原生的bind()方法的浏览器有ie9+/Firefox 4+/Chrome
- 函数柯里化
用于创建已经设置好了一个或多个参数的函数。使用一个闭包返回一个函数。
- 1
- 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
- 1
- 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
- 防篡改对象
ECMAScript 5定义了防篡改对象,一旦设置了防篡改就无法撤消。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 高级定时器
在5处创建时间间隔为200的定时器,在205处添加到队列中,但是onclick事件处理函数处理到300才开始执行每个定时器代码,在405处又给队列添加了一个定时器任务,在605处由于队列里已经有一个定时器任务了所以不会被添加到队列中。结果在5处添加的定时器任务一执行完,405处的定时器任务立马开始执行跳过了一个时间间隔。
- 1
- 2
- 3
- 1
- 2
- 3
离线应用与客户端存储
支持离线Web应用开发是HTML5的一个重点。所谓离线Web应用就是在设备不能上网的情况下仍然可以运行的应用。
- 离线检测
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 应用缓存
appcache,从浏览器缓存中分出来一块缓存区。想要在这个缓存中保存数据,可以使用一个描述文件:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
- 1
- 1
文件的MIME类型必须是text/cache-manifest,扩展名以前用manifest,现在用aapcache,applicationCache对象控制应用缓存。
- 数据存储
- Cookie 限制个数和长度,信息构成:名称、值、域、路径、失效时间、安全标志
- 1
- 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
- 1
- 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
- Web存储机制
克服由cookie带来的一些限制,当数据需要被严格控制在客户端时,无须持续地将数据发回服务器。Web Storage两个目标:
- 提供一种在cookie之外存储会话数据的途径;
- 提供一种存储大量可以跨会话存在的数据的机制。
sessionStorage对象存储特定于某个会话的数据,该数据保持到浏览器关闭,但可以跨页面刷新而存在,浏览器崩溃重启后依然可用(IE不行)。sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的,且数据只能由最初给对象存储数据的页面访问到。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
globalStorage需要指定域访问数据,是依据发起请求的页面的域名、协议和端口来限制的,如http和https就不同。globalStorage属性中的数据会一直保留在磁盘上,非常适合在客户端存储文档或者长期保存用户偏好设置。
- 1
- 2
- 3
- 1
- 2
- 3
localStorage对象在修订过的Html5规范中作为取代globalStorage。要访问同一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口上。同globalStorage中的数据一样,数据保留到通过Javascript删除或者用户清除浏览器缓存。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- storage事件,webkit尚不支持
- domain 发生变化的存储空间的域名,ie8和firefox只支持该属性
- key 设置或删除的键名
- newValue 如果是set则为新值,如果是remove则为null
- oldValue 键被更改之前的值
- 1
- 2
- 3
- 1
- 2
- 3
大多数对数据存储限制是5MB,Chrome和Safari以及iOS版Safari和Android版WebKit是2.5MB.
- IndexedDB
创建一套API方便保存和读取JavaScript对象,同时还支持查询及搜索。操作都是异步的,需要注册onerror或onsuccess事件处理程序。
- 1
- 1
IndexedDB是一种类似SQL数据库的结构化数据存储机制。数据不是存在表中而是对象存储空间中。创建对象存储空间时定义一个键后就可以添加数据,可以使用游标在空间中查询特定对象,索引则是为了提高查询速度而基于特定的属性创建的。唯一需要注意的是不要存储敏感数据,因为数据缓存不会加密。
最佳实践
- 可维护性
- 可理解性
- 直观性
- 可适应性
- 可扩展性
- 可调度性
- 代码约定
- 可读性 函数和方法都应该包含一个注释,描述其目的和使用的算法,参数和返回;大段代码、Hack和复杂算法也需要一个描述任务的注释;变量以名词,函数以动词开头不要担心长度,长度可通过压缩缓解
- 松散耦合 直接写在HTML中JavaScript,使用包含内联代码的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- 如果值应为引用类型,使用instanceof检查其构造函数
- 如果值应为一个基本类型,使用typeof检查其类型
- 如果希望对象包含某个特定的方法名,则使用typeof操作符确保指定qkpbr方法存在于对象上。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 性能
- 注意作用域 1.避免全局查找,使用全局变量和函数肯定比局部的开销更大,因为涉及作用域链上的查找。将在一个函数中会用到多次的全局对象存储为局部变量总是没错的。一旦多次用到对象属性也是一样的,应该将其存储在局部变量中,第一次访问是O(n),后续就是O(1)了。
- 减值迭代
- 简化终止条件 避免如o(n)的属性查找
- 使用后测试循环
- 展开循环
- 避免双重解释 如eval、new Function、setTimeout(“alert(‘Hello’);”,500)等
- 优化DOM交互,文档碎片、大的DOM更改innerHTML更快、可以将事件处理程序附加到更高层的地方负责多个目标的事件处理。
新兴API
- requestAnimationFrame()
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒重绘60次,大多浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超了也不会提升用户体验。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于17ms. - Page Visibility API 页面最小化了或者隐藏在了其他标签后面,那么有些功能比如轮询或都动画效果可以停下来。
- Geolocation API 定位
- File API FileReader/读取部分内容/对象URL/读取拖放文件/使用XHR上传文件
- Web计时
- Web Workers