一.JS的简介:
1.宿主环境:浏览器、node、adobe flash;
2.DOM(文档对象模型)是针对 XML 但经过扩展用于 HTML 的应用程序编程接口(API):
DOM0级:即DHTML; DOM1级:主要映射文档结构; DOM2级:增加视图,样式,事件,遍历和范围; DOM3级:增加验证等方法;
3.BOM(浏览器对象模型)
二.在html中使用js
1.script的属性:defer(html4.01):脚本延迟至页面加载完成后执行;async(html5):不必等待脚本下载,异步加载页面其他内容;
三.基本概念:
1.严格模式:"use strict";
2.用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量;
3.ECMAScript5 中有 5 种基本数据类型: Undefined 、 Null 、 Boolean 、 Number和 String 。还有 1种复杂数据类型—— Object;
a.typeof操作符:“undefined”、'boolean'、'string'、'number'、'object'、'function'.(null会被认为是一个空的对象引用,返回object);
b.自动数据类型转换:true(true,非空字符串,非0数字,任何对象) false(false,空字符串,0和NaN,null,undefined);
c.转义字符:\n(换行),\t(制表),\b(空格),\r(回车),\\, \' ,\";
d.将一个值转为字符串,可以+'';
e.object的属性和方法:constructor hasOwnProperty('') isPrototypeOf(object) propertyIsEnumerable('propertyName') toLocaleString()
toString() valueOf()(返回对象的字符串、数值或布尔值表示,通常和toString()返回值相同)。
4.操作符:++a(先自加再运算);a++(先运算再自加);
5.语句:a.do-while:(至少执行一次,无论条件)
var i = 0;
do {
i += 2;
} while (i < 10);
alert(i);
b.label语句:(可在将来由break或continue语句引用)
start: for (var i=0; i < count; i++) { alert(i); }
c.with语句:(将代码作用域设置到一个特定的对象中,一般不用)
6.函数:可以用arguments.length模拟重载;
四.变量、作用域和内存问题:
1.当复制保存着对象的某个变量时,操作的是对象的引用;为对象添加属性时,操作的是对象本身。
2.参数的传递是按值传递(无论基本类型还是引用类型):即使在函数内部修改了参数的值,但原始的引用仍然保持未变
function setName(obj) { obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
可以把 ECMAScript 函数的参数想象成局部变量。
3.检测基本数据类型使用typeof ,检测引用类型使用instanceof;
4.a.执行环境(上下文)定义了变量和函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象,保存着环境中定义的所有变量和函数。(解析器使用);每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境;标识符解析是沿着作用域链一级一级地搜索标识符的过程。
b.catch块和with语句可以延长作用域链;
c.垃圾收集:标记清除与引用计数;管理内存:解除引用,适用于大多数全局变量和全局对象的属性(设置为null)
5.基本类型的值在内存中占据固定的大小空间,因此保存在栈内存中;引用类型的值是对象,保存在堆内存中;
五、引用类型:
1.Object:点与花括号
alert(person["name"]); //"Nicholas" alert(person.name); //"Nicholas" var propertyName = "name"; alert(person[propertyName]); //"Nicholas" person["first name"] = "Nicholas";
2.Array:
arr.join(''):返回包含所有数组项的字符串;
push() 方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项.
shift() ,它能够移除数组中的第一个项并返回该项,同时将数组长度减 1。
unshift() 它能在数组前端添加任意个项并返回新数组的长度。
reverse() 和 sort() 方法的返回值是经过排序之后的数组。
arr.sort(function(a,b){return a-b})
concat():这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
var colors = ["red", "green", "blue"]; var colors2 = colors.concat("yellow", ["black", "brown"]); alert(colors); //red,green,blue alert(colors2); //red,green,blue,yellow,black,brown
slice:不影响原始数组
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:始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。
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,返回的数组中只包含一项
indexOf()和lastIndexOf():
var numbers = [1,2,3,4,5,4,3,2,1]; alert(numbers.indexOf(4)); //3 alert(numbers.lastIndexOf(4)); //5 alert(numbers.indexOf(4, 4)); //5 alert(numbers.lastIndexOf(4, 4)); //3 var person = { name: "Nicholas" }; var people = [{ name: "Nicholas" }]; var morePeople = [person]; alert(people.indexOf(person)); //-1 alert(morePeople.indexOf(person)); //0
迭代方法:每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响 this 的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身:(IE9+)
every() :对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true 。
filter() :对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
forEach() :对数组中的每一项运行给定函数。这个方法没有返回值。
map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
some() :对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true 。
以上方法都不会修改数组中的包含的值。
var numbers = [1,2,3,4,5,4,3,2,1]; var everyResult = numbers.every(function(item, index, array){ return (item > 2); }); alert(everyResult); //false var someResult = numbers.some(function(item, index, array){ return (item > 2); }); alert(someResult); //true var numbers = [1,2,3,4,5,4,3,2,1]; var filterResult = numbers.filter(function(item, index, array){ return (item > 2); }); alert(filterResult); //[3,4,5,4,3] var numbers = [1,2,3,4,5,4,3,2,1]; var mapResult = numbers.map(function(item, index, array){ return item * 2; }); alert(mapResult); //[2,4,6,8,10,8,6,4,2] var numbers = [1,2,3,4,5,4,3,2,1]; numbers.forEach(function(item, index, array){ //执行某些操作 });
归并方法:reduce() 方法从数组的第一项开始,逐个遍历到最后。而 reduceRight() 则从数组的最后一项开始,向前遍历到第一项。
var values = [1,2,3,4,5]; var sum = values.reduce(function(prev, cur, index, array){ return prev + cur; }); alert(sum); //15
3.Date类型:
//取得开始时间 var start = Date.now(); //调用函数 doSomething(); //取得停止时间 var stop = Date.now(), result = stop – start;
4.RegeExp类型:
var reg= / pattern / flags ;
flags:i g m(多行模式,到文本末尾后还会继续查找下一行)
/* * 匹配第一个"bat"或"cat",不区分大小写 */ var pattern1 = /[bc]at/i; /* * 与 pattern1 相同,只不过是使用构造函数创建的 */ var pattern2 = new RegExp("[bc]at", "i");
实例属性:
var pattern1 = /\[bc\]at/i; alert(pattern1.global); //false alert(pattern1.ignoreCase); //true alert(pattern1.multiline); //false alert(pattern1.lastIndex); //0 alert(pattern1.source); //"\[bc\]at"
实例方法1:exec():返回包含第一个匹配项信息的数组,没有的话返回null。额外属性input和index
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" alert(matches[2]); // " and baby"
若有g,则每次返回一个匹配项;若无g,则始终返回第一个匹配项;
var pattern2 = /.at/g; var matches = pattern2.exec(text); alert(matches.index); //0 alert(matches[0]); //cat alert(pattern2.lastIndex); //3 matches = pattern2.exec(text); alert(matches.index); //5 alert(matches[0]); //bat alert(pattern2.lastIndex); //8
实例方法二:test():返回布尔值;
var text = "000-00-0000"; var pattern = /\d{3}-\d{2}-\d{4}/; if (pattern.test(text)){ alert("The pattern was matched."); }
构造函数属性:。。。。。。还有多达 9 个用于存储捕获组的构造函数属性。访问这些属性的语法是 RegExp.$1 、 RegExp.$2 … RegExp.$9 ,分别用于存储第一、第二……第九个匹配的捕获组。在调用 exec() 或 test() 方法时,这些属性会被自动填充。
var text = "this has been a short summer"; var pattern = /(..)or(.)/g; if (pattern.test(text)){ alert(RegExp.$1); //sh alert(RegExp.$2); //t }
5.Function类型:
函数是对象,函数名是指针。函数没有重载。函数声明会提升
function sum(num1, num2){ return num1 + num2; } alert(sum(10,10)); //20 var anotherSum = sum; alert(anotherSum(10,10)); //20 sum = null; alert(anotherSum(10,10)); //20
以上代码即使将 sum 设置为 null ,让它与函数“断绝关系”,但仍然可以正常调用anotherSum()。
作为值的函数:以下例子可以用传入的属性选择排序方式;
function createComparisonFunction(propName) { return function(obj1, obj2){ var value1 = obj1[propName]; var value2 = obj2[propName]; if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } }; } var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; data.sort(createComparisonFunction("name")); alert(data[0].name); //Nicholas data.sort(createComparisonFunction("age")); alert(data[0].name); //Zachary
函数内部属性:arguments的callee属性,该属性指向拥有这个arguments对象的函数;可以利用它消除紧密耦合,如下阶乘例子:
function factorial(num){ if (num <=1) { return 1; } else { return num * arguments.callee(num-1) } }
caller:这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null .
function outer(){ inner(); } function inner(){ alert(arguments.callee.caller);//或者alert(inner.caller) } outer();
函数的属性和方法:
属性:length:表示希望函数接收的参数的个数(形参); prototype:保存各种实例方法的真正所在;
方法(非继承的):apply和call()和bind()
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 function callSum(num1, num2){ return sum.call(this, num1, num2); } alert(callSum(10,10)); //20
window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } var objectSayColor = sayColor.bind(o); objectSayColor(); //blue
基本包装类型:(Boolean Number String)
var s1 = "some text"; var s2 = s1.substring(2); //以上代码的内部实现如下: var s1 = new String("some text"); var s2 = s1.substring(2); s1 = null;
Number: toFixed(0-20):显示0-20位小数,自动舍入; toExponential():返回指数表示法(e) toPrecision() :接收一个参数,即表示数值的所有数字的位数。会根据要处理的数值决定到底是调用 toFixed() 还是调用 toExponential() 。
String类型:length属性
方法:
var stringValue = "hello world"; alert(stringValue.charAt(1)); //"e" alert(stringValue.charCodeAt(1)); // 输出"101" alert(stringValue[1]); //"e" var stringValue = "hello "; var result = stringValue.concat("world", "!"); alert(result); //"hello world!" alert(stringValue); //"hello //不修改原值 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" //不修改原值 var stringValue = "hello world"; 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)); //"" (空字符串) //substring不适用于负数参数 var stringValue = "hello world"; alert(stringValue.indexOf("o", 6)); //7 alert(stringValue.lastIndexOf("o", 6)); //4 var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; var positions = new Array(); var pos = stringValue.indexOf("e"); while(pos > -1){ positions.push(pos); pos = stringValue.indexOf("e", pos + 1); } alert(positions); //"3,24,32,35,52" var stringValue = " hello world "; var trimmedStringValue = stringValue.trim(); alert(stringValue); //" hello world " alert(trimmedStringValue); //"hello world" var stringValue = "hello world"; alert(stringValue.toLocaleUpperCase()); //"HELLO WORLD" alert(stringValue.toUpperCase()); //"HELLO WORLD" alert(stringValue.toLocaleLowerCase()); //"hello world" alert(stringValue.toLowerCase()); //"hello world" 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 var text = "cat, bat, sat, fat"; var pos = text.search(/at/); alert(pos); //1 //返回at在字符串中第一次出现的位置。 var text = "cat, bat, sat, fat"; var result = text.replace("at", "ond"); alert(result); //"cond, bat, sat, fat" result = text.replace(/at/g, "ond"); alert(result); //"cond, bond, sond, fond" var text = "cat, bat, sat, fat"; result = text.replace(/(.at)/g, "word ($1)"); alert(result); //word (cat), word (bat), word (sat), word (fat) 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(/[^\,]+/); //["", ",", ",", ",", ""] alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
单体内置对象:
Global:
encodeURIComponent():编码; decodeURIComponent():解码; eval();
window
Math:
var values = [1, 2, 3, 4, 5, 6, 7, 8]; var max = Math.max.apply(Math, values);
alert(Math.ceil(25.9)); //26 alert(Math.ceil(25.5)); //26 alert(Math.ceil(25.1)); //26 alert(Math.round(25.9)); //26 alert(Math.round(25.5)); //26 alert(Math.round(25.1)); //25 alert(Math.floor(25.9)); //25 alert(Math.floor(25.5)); //25 alert(Math.floor(25.1)); //25
function selectFrom(min, max) { var choices = max - min + 1; return Math.floor(Math.random() * choices + min); } var num = selectFrom(2, 10); alert(num); // 介于 2 和 10 之间(包括 2 和 10)的一个数值
六.面向对象的程序设计:
1.属性类型(属性的特性):
a.数据属性:
[[Configurable]] [[Enumerable]] [[Writable]] [[Value]](默认undefined)。
Object.defineProperty(person,'name',{configurable:false,value:'jiuye'}) (IE9+)
若将configurable设置为false,则除了writable之外的特性都无法再修改,包括configurable;
在调用 Object.defineProperty() 方法时,如果不指定, configurable 、 enumerable 和writable 特性的默认值都是 false 。
b.访问器属性:
[[Configurable]] [[Enumerable]] [[Get]] [[Set]]
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
defineProperties:
var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } } });
Object.getOwnPropertyDescriptor():
var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); alert(descriptor.value); //2004 alert(descriptor.configurable); //false alert(typeof descriptor.get); //"undefined" var descriptor = Object.getOwnPropertyDescriptor(book, "year"); alert(descriptor.value); //undefined alert(descriptor.enumerable); //false alert(typeof descriptor.get); //"function"
2.创建对象:
a.工厂模式:
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,);
var person2 = createPerson("Greg", 27, "Doctor");
b.构造函数模式:
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");
new后经历的步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
c.原型模式:
实例内部存在(__proto__)指向构造函数的原型对象;
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
in 操作符只要通过对象能够访问到属性就返回 true , hasOwnProperty() 只在属性存在于实例中时才返回 true。 ,因此只要 in 操作符返回 true 而 hasOwnProperty() 返回 false ,就可以确定属性是原型中的属性。
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); } function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person = new Person(); alert(hasPrototypeProperty(person, "name")); //true person.name = "Greg"; alert(hasPrototypeProperty(person, "name")); //false
Object.keys():返回一个包含所有可枚举属性的字符串数组。
Object.getOwnPropertyNames():返回所有实例属性,无论它是否可枚举。
若用对象字面量创建新对象,记得重新设置constructor,以及设置constructor的Enumerable为false。
原型具有动态性:由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上
反映出来——即使是先创建了实例后修改原型也照样如此。但要注意,如果重写原型,则会断开现有原型与之前存在的对象实例的关系。(他们仍然引用的是最初的原型)
d.组合使用构造函数和原型模式:
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
e.动态原型模式:
function Person(name, age, job){ //属性 this.name = name; this.age = age; this.job = job; // 方法 f (typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); }; } } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName();
f.寄生构造函数模式:表面上很像普通构造函数。
g.稳妥构造函数模式:适合需要安全执行环境的代码中。
function Person(name, age, job){ //创建要返回的对象 var o = new Object(); //可以在这里定义私有变量和函数 //添加方法 o.sayName = function(){ alert(name); }; //返回对象 return o; }
3.继承:
a.原型链:SubType.prototype = new SuperType();搜索步骤:1)搜索实例;2)搜索 SubType.prototype ;3)搜索 SuperType.prototype。
4).搜索Object.Prototype
如何确定原型和实例的关系:
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true
在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链。
原型链的问题:1.原先的实例属性会变成了现在的原型属性; 2.无法向父类传递参数。
b.借用构造函数:(很少单独使用)
function SuperType(name){ this.name = name; } function SubType(){ //继承了 SuperType,同时还传递了参数 SuperType.call(this, "Nicholas"); //实例属性 this.age = 29; } var instance = new SubType(); alert(instance.name); //"Nicholas"; alert(instance.age); //29
c.组合继承:(常用)(将原型链和借用构造函数组合)
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.constructor = SubType; 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
d.原型式继承:
Object.create()
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person, { name: { value: "Greg" } }); alert(anotherPerson.name); //"Greg"
只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
e.寄生式继承:。创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
不能函数复用。
function createAnother(original){ var clone = object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
f.寄生组合式继承:(引用类型最理想的继承范式)
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 } 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; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); };
七、函数表达式:
函数表达式创建的函数是匿名函数,在把函数当成值来使用的情况下,都可以使用匿名函数。
a.递归:
//函数表达式递归 var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num-1); } }); //普通声明递归 function factorial(num){ if (num <= 1){ return 1; } else { return num * arguments.callee(num-1); } }
b.闭包:
当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象。
在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。使用闭包可以在 JavaScript中模仿块级作用域。
function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(num){ return function(){ return num; }; }(i); } return result; }
垃圾回收:
//HTML元素无法销毁 function assignHandler(){ var element = document.getElementById("someElement"); element.onclick = function(){ alert(element.id); }; } //修改后: function assignHandler(){ var element = document.getElementById("someElement"); var id = element.id; element.onclick = function(){ alert(id); }; element = null; }
c.模拟块级作用域:
(function(){ var now = new Date(); if (now.getMonth() == 0 && now.getDate() == 1){ alert("Happy new year!"); } })(); function outputNumbers(count){ (function () { for (var i=0; i < count; i++){ alert(i); } })(); alert(i); //导致一个错误! }
d.私有变量:任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
把有权访问私有变量和私有函数的公有方法称为特权方法。
function MyObject(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权方法 this.publicMethod = function (){ privateVariable++; return privateFunction(); }; }
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"
以上代码的构造函数中定义了两个特权方法: getName() 和 setName() 。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量 name 。但在 Person 构造函数外部,没有任何办法访问 name 。由于这两个方法是在构造函数内部定义的,它们作为闭包能够通过作用域链访问 name 。私有变量 name在 Person 的每一个实例中都不相同,因为每次调用构造函数都会重新创建这两个方法。缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题。
静态私有变量:
这个模式与在构造函数中定义特权方法的主要区别,就在于私有变量和函数是由实例共享的。由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法,作为一个闭包,总是保存着对包含作用域的引用。
(function(){ var name = ""; Person = function(value){ name = value; }; Person.prototype.getName = function(){ return name; }; Person.prototype.setName = function (value){ name = value; }; })(); var person1 = new Person("Nicholas"); alert(person1.getName()); //"Nicholas" person1.setName("Greg"); alert(person1.getName()); //"Greg" var person2 = new Person("Michael"); alert(person1.getName()); //"Michael" alert(person2.getName()); //"Michael"
模块模式:
var singleton = function(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权/公有方法和属性 return { publicProperty: true, publicMethod : function(){ privateVariable++; return privateFunction(); } }; }();
从本质上来讲,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有
变量时是非常有用的。
增强的模块模式:
var singleton = function(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //创建对象 var object = new CustomType(); //添加特权/公有属性和方法 object.publicProperty = true; object.publicMethod = function(){ privateVariable++; return privateFunction(); }; //返回这个对象 return object; }();
这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。
八、BOM
1.window对象:既是通过 JavaScript 访问浏览器窗口的一个接口,又是 ECMAScript 规定的 Global 对象。与全局对象的区别在于window对象的属性可以delete。
a.窗口位置:(无法取得精确坐标值)
var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX; var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
窗口移动:(可能被浏览器禁用)
b.窗口大小:
IE9+、Firefox、Safari、Opera 和 Chrome 均为此提供了 4个属性: innerWidth 、 innerHeight 、 outerWidth 和 outerHeight 。在 IE9+、Safari和 Firefox中, outerWidth 和 outerHeight 返回浏览器窗口本身的尺寸(无论是从最外层的 window 对象还是从某个框架访问)。在Opera中,这两个属性的值表示页面视图容器 的大小。而innerWidth 和 innerHeight则表示该容器中页面视图区的大小(减去边框宽度)。在 Chrome 中, outerWidth 、 outerHeight 与innerWidth 、 innerHeight 返回相同的值,即视口(viewport)大小而非浏览器窗口大小。
取得页面视口的大小:
var 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; } }
窗口移动:
//将窗口移动到屏幕左上角 window.moveTo(0,0); //将窗向下移动 100 像素 window.moveBy(0,100); //将窗口移动到(200,300) window.moveTo(200,300); //将窗口向左移动 50 像素 window.moveBy(-50,0)
窗口大小调整:(可能被浏览器禁用)
//调整到 100×100 window.resizeTo(100, 100); //调整到 200×150 window.resizeBy(100, 50); //调整到 300×300 window.resizeTo(300, 300);
c.导航和打开窗口:
window.open():
var a=window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");
a.close();
以下代码检测出调用 window.open() 打开的弹出窗口是不是被屏蔽了:
var blocked = false; try { var wroxWin = window.open("http://www.wrox.com", "_blank"); if (wroxWin == null){ blocked = true; } } catch (ex){ blocked = true; } if (blocked){ alert("The popup was blocked!"); }
d.间歇调用和超时调用:
setTimeout:全局作用域中执行
//设置超时调用 var timeoutId = setTimeout(function() { alert("Hello world!"); }, 1000); //注意:把它取消 clearTimeout(timeoutId);
setInterval:
var num = 0; var max = 10; var intervalId = null; function incrementNumber() { num++; //如果执行次数达到了 max 设定的值,则取消后续尚未执行的调用 if (num == max) { clearInterval(intervalId); alert("Done"); } } intervalId = setInterval(incrementNumber, 500);
更好的方式是用setTimeOut模拟:
var num = 0; var max = 10; function incrementNumber() { num++; // 如果执行次数未达到 max 设定的值,则设置另一次超时调用 if (num < max) { setTimeout(incrementNumber, 500); } else { alert("Done"); } } setTimeout(incrementNumber, 500);
e.系统对话框:
alert() confirm() prompt() find() print();
2.location对象:
window.location 和 document.location 引用的是同一个对象。
属性:hash host("www.wrox.com:80") hostname("www.wrox.com") href("http:/www.wrox.com") pathname("/WileyCDA/")
port('8080') protocol('http') search("?q=javascript").
a.查询字符串参数
function getQueryStringArgs(){ //取得查询字符串并去掉开头的问号 var qs = (location.search.length > 0 ? location.search.substring(1) : ""), //保存数据的对象 args = {}, //取得每一项 items = qs.length ? qs.split("&") : [], item = null, name = null, value = null, //在 for 循环中使用 i = 0, len = items.length; //逐个将每一项添加到 args 对象中 for (i=0; i < len; i++){ item = items[i].split("="); name = decodeURIComponent(item[0]); value = decodeURIComponent(item[1]); if (name.length) { args[name] = value; } } return args; } //假设查询字符串是?q=javascript&num=10 var args = getQueryStringArgs(); alert(args["q"]); //"javascript" alert(args["num"]); //"10"
b.位置操作:
location.href = "http://www.wrox.com"; //假设初始 URL 为 http://www.wrox.com/WileyCDA/ //将 URL 修改为"http://www.wrox.com/WileyCDA/#section1" location.hash = "#section1"; //将 URL 修改为"http://www.wrox.com/WileyCDA/?q=javascript" location.search = "?q=javascript"; //将 URL 修改为"http://www.yahoo.com/WileyCDA/" location.hostname = "www.yahoo.com"; //将 URL 修改为"http://www.yahoo.com/mydir/" location.pathname = "mydir"; //将 URL 修改为"http://www.yahoo.com:8080/WileyCDA/" location.port = 8080; 每次修改 location 的属性( hash 除外),页面都会以新 URL 重新加载
location.replace("http://www.wrox.com/"):无法后退;
location.reload(); //重新加载(有可能从缓存中加载)
location.reload(true); //重新加载(从服务器重新加载)
3.navigator对象:
a.检测插件:
//检测所有浏览器中的 Flash
function hasFlash(){
var result = hasPlugin("Flash");
if (!result){
result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
}
return result;
}
//检测所有浏览器中的 QuickTime
function hasQuickTime(){
var result = hasPlugin("QuickTime");
if (!result){
result = hasIEPlugin("QuickTime.QuickTime");
}
return result;
}
//检测 Flash
alert(hasFlash());
//检测 QuickTime
alert(hasQuickTime());
b.注册处理程序:
registerContentHandler() 和 registerProtocolHandler() 方法。这两个方法可以让一个站点指明它可以处理特定类型的信息。(MIME)
4.screen对象:
window.resizeTo(screen.availWidth, screen.availHeight)等;
5.history对象:
//后退一页 history.go(-1); //前进一页 history.go(1); //前进两页 history.go(2); //跳转到最近的 wrox.com 页面 history.go("wrox.com"); //跳转到最近的 nczonline.net 页面 history.go("nczonline.net"); //后退一页 history.back(); //前进一页 history.forward();
if (history.length == 0){
//这应该是用户打开窗口后的第一个页面
}
九、客户端监测:
先设计最通用的方案,然后再使用特定于浏览器的技术增强该方案。
1.能力监测:(!!一般用来将后面的表达式强制转换为布尔类型的数据(boolean))
function isHostMethod(object, property) { var t = typeof object[property]; return t=='function' || (!!(t=='object' && object[property])) || t=='unknown'; } 可以像下面这样使用这个函数: result = isHostMethod(xhr, "open"); //true result = isHostMethod(xhr, "foo"); //false
//确定浏览器是否支持 Netscape 风格的插件 var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length); //确定浏览器是否具有 DOM1 级规定的能力 var hasDOM1 = !!(document.getElementById && document.createElement && document.getElementsByTagName);
应该将能力检测作为确定下一步解决方案的依据,而不是用它来判断用户使用的是什么浏览器。
2.怪癖监测:
怪癖检测是想要知道浏览器存在什么缺陷。
例如,IE8 及更早版本中存在一个 bug,即如果某个实例属性与 [[Enumerable]] 标记为 false 的某个原型属性同名,那么该实例属性将不会出现在
fon-in 循环当中。可以使用如下代码来检测这种“怪癖”。
var hasDontEnumQuirk = function(){ var o = { toString : function(){} }; for (var prop in o){ if (prop == "toString"){ return false; } } return true; }();
3.用户代理监测:
通过 JavaScript 的 navigator.userAgent 属性访问检测用户代理字符串来确定实际使用的浏览器。
IE模仿mozilla webkit(safari)模仿mozilla webkit(chrome)模仿mozilla opera有自己独自的版本号开头。
ios和Android的默认浏览器机遇webkit
应该主要检测五大呈现引擎:IE、Gecko、WebKit、KHTML 和 Opera。
检测顺序:opera. webkit. KHTML. Gecko. IE.
在某些条件下,平台可能是必须关注的问题。那些具有各种平台版本的浏览器在不同的平台下可能会有不同的问题。目前的三大主流平台是 Windows、Mac 和 Unix(包括各种 Linux)。
识别windows
识别移动设备
识别游戏设备
完整代码(略):
使用场景:优先使用能力监测和怪癖监测。用户代理监测一般适用于下列情形:
a.不能直接准确地使用能力检测或怪癖检测。例如,某些浏览器实现了为将来功能预留的存根
(stub)函数。在这种情况下,仅测试相应的函数是否存在还得不到足够的信息。
b.同一款浏览器在不同平台下具备不同的能力。这时候,可能就有必要确定浏览器位于哪个平
台下。
c.为了跟踪分析等目的需要知道确切的浏览器。
十、DOM(针对 HTML 和 XML 文档的一个 API)
1.节点层次:
a.node类型:
JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型都共享着相同的基本属性和方法。每个节点都有一个 nodeType 属性,用于表明节点的类型。对于元素节点, nodeName 中保存的始终都是元素的标签名,而 nodeValue 的值则始终为 null 。
节点关系:
每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。 NodeList 是一种类数组对象。
第一个节点的 previousSibling 属性值为 null ,而列表中最后一个节点的 nextSibling 属性的值同样也为 null。
if (someNode.nextSibling === null){ alert("Last node in the parent’s childNodes list."); } else if (someNode.previousSibling === null){ alert("First node in the parent’s childNodes list."); }
父节点的 firstChild 和 lastChild属性分别指向其 childNodes 列表中的第一个和最后一个节点。 hasChildNodes() 在节点包含一或多个子节点的情况下返回 true 。这是比查询 childNodes列表的 length 属性更简单的方法。
操作节点:
appendChild():
任何 DOM 节点也不能同时出现在文档中的多个位置上。因此,如果在调用 appendChild() 时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点。
var returnedNode = someNode.appendChild(newNode); alert(returnedNode == newNode); //true alert(someNode.lastChild == newNode); //true
insertBefore():接受两个参数:要插入的节点和作为参照的节点。
//插入后成为最后一个子节点 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():接受一个布尔值参数,表示是否执行深复制。(深复制(true):复制节点和整个子节点数;浅复制:只复制节点本身)
IE会复制事件处理程序(应该先移除事件处理程序)。
b.Document类型:
document 对象是 HTMLDocument的一个实例,表示整个 HTML 页面。而且, document 对象是 window 对象的一个属性,因此可以将其作为全局对象来访问。 Document 节点具有下列特征:
nodeType 的值为 9;
nodeName 的值为 "#document" ;
nodeValue 的值为 null ;
parentNode 的值为 null ;
ownerDocument 的值为 null ;
var html = document.documentElement; //取得对<html>的引用 alert(html === document.childNodes[0]); //true alert(html === document.firstChild); //true var body = document.body; //取得对<body>的引用 var doctype = document.doctype; //取得对<!DOCTYPE>的引用 //取得文档标题 var originalTitle = document.title; //设置文档标题 document.title = "New page title"; //取得完整的 URL var url = document.URL; //取得域名 var domain = document.domain; //取得来源页面的 URL var referrer = document.referrer;
URL 与 domain 属性是相互关联的。例如,如果 document.URL 等于 http://www.wrox.com/WileyCDA/,那么 document.domain 就等于 www.wrox.com。
//假设页面来自于 p2p.wrox.com 域 document.domain = "wrox.com"; //松散的(成功) document.domain = "p2p.wrox.com"; //紧绷的(出错!)
var allElements = document.getElementsByTagName("*");可以取得文档所有元素。
getElementsByName():可以用于单选按钮的name值;
document.anchors ,包含文档中所有带 name 特性的 <a> 元素;
document.links ,包含文档中所有带 href 特性的 <a> 元素;
document.forms ,包含文档中所有的 <form> 元素,与 document.getElementsByTagName("form")得到的结果相同;
document.images ,包含文档中所有的 <img> 元素,与 document.getElementsByTagName("img") 得到的结果相同;
var hasXmlDom = document.implementation.hasFeature("XML", "1.0"):检测dom功能;
如果在文档加载结束后再调用 document.write() ,那么输出的内容将会重写整个页面; writeln()在最后加换行符\n
window.onload = function(){ document.write("Hello world!"); };
c.Element类型:用于表现 XML或 HTML元素,提供了对元素标签名、子节点及特性的访问。 Element 节点具有以下特征:
nodeType 的值为 1;
nodeName 的值为元素的标签名;
nodeValue 的值为 null ;
parentNode 可能是 Document 或 Element ;
其子节点可能是 Element 、 Text 、 Comment 、 ProcessingInstruction 、 CDATASection 或EntityReference 。
if (element.tagName == "div"){ //不能这样比较,很容易出错! //在此执行某些操作 } if (element.tagName.toLowerCase() == "div"){ //这样最好(适用于任何文档) //在此执行某些操作 }
取得特性:
每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。操作特性的DOM 方法主要有三个,分别是 getAttribute() 、 setAttribute() 和 removeAttribute() 。
在通过 JavaScript 以编程方式操作 DOM 时,开发人员经常不使用 getAttri-bute() ,而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用 getAttribute() 方法。
div.setAttribute("id", "someOtherId"); div.setAttribute("class", "ft"); div.setAttribute("title", "Some other text"); div.removeAttribute("class");
attributes 属性:一般用于遍历。
var div = document.createElement("div"); div.id = "myNewDiv"; div.className = "box"; document.body.appendChild(div);
IE不将空白符计入节点数,其他浏览器会计入节点数。
Text类型:包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML 字符,但不能包含 HTML 代码。 Text 节点具有以下特征:
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 为止处的字符串。
除了这些方法之外,文本节点还有一个 length 属性,保存着节点中字符的数目。而且,nodeValue.length 和 data.length 中也保存着同样的值。
创建文本节点:
var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element);
normalize():可以将所有文本节点合并成一个节点。
splitText():分割文本节点;
Comment类型:(注释):
nodeType 的值为 8;
nodeName 的值为 "#comment" ;
nodeValue 的值是注释的内容;
parentNode 可能是 Document 或 Element ;
不支持(没有)子节点。
Attr类型(特性):
nodeType 的值为 2;
nodeName 的值是特性的名称;
nodeValue 的值是特性的值;
parentNode 的值为 null ;
在 HTML 中不支持(没有)子节点;
在 XML 中子节点可以是 Text 或 EntityReference 。
2.DOM操作技术:
动态脚本:
function loadScript(url){ var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; document.body.appendChild(script); } loadScript("client.js");
动态样式:
function loadStyles(url){ var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = url; var head = document.getElementsByTagName("head")[0]; head.appendChild(link); } loadStyles("styles.css");
操作表格:
<table border="1" width="100%"> <tbody> <tr> <td>Cell 1,1</td> <td>Cell 2,1</td> </tr> <tr> <td>Cell 1,2</td> <td>Cell 2,2</td> </tr> </tbody> </table> 核心DOM方法: //创建 table var table = document.createElement("table"); table.border = 1; table.width = "100%"; //创建 tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); //创建第一行 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); //创建第二行 var row2 = document.createElement("tr"); tbody.appendChild(row2); var cell1_2 = document.createElement("td"); cell1_2.appendChild(document.createTextNode("Cell 1,2")); row2.appendChild(cell1_2); var cell2_2= document.createElement("td"); cell2_2.appendChild(document.createTextNode("Cell 2,2")); row2.appendChild(cell2_2); //将表格添加到文档主体中 document.body.appendChild(table); 使用特殊方法: //创建 table var table = document.createElement("table"); table.border = 1; table.width = "100%"; //创建 tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); // 创建第一行 tbody.insertRow(0); tbody.rows[0].insertCell(0); tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1")); tbody.rows[0].insertCell(1); tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1")); // 创建第二行 tbody.insertRow(1); tbody.rows[1].insertCell(0); tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2")); tbody.rows[1].insertCell(1); tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2")); //将表格添加到文档主体中 document.body.appendChild(table);
使用 NodeList:
//死循环 var divs = document.getElementsByTagName("div"), i, div; for (i=0; i < divs.length; i++){ div = document.createElement("div"); document.body.appendChild(div); } //用len保存divs.length后即可; var divs = document.getElementsByTagName("div"), i, len, div; for (i=0, len=divs.length; i < len; i++){ div = document.createElement("div"); document.body.appendChild(div); }
十一、DOM扩展:
querySelector():
querySelectorAll():返回的值实际上是带有所有属性和方法的 NodeList ,而其底层实现则类似于一组元素的快照,而非不断对文档进行搜索的动态查询。这样实现可以避免使用 NodeList 对象通常会引起的大多数性能问题。只要传给 querySelectorAll() 方法的 CSS 选择符有效,该方法都会返回一个 NodeList 对象,而不管找到多少匹配的元素。如果没有找到匹配的元素, NodeList 就是空的。
matchesSelector():返回boolean;
getElementsByClassName():
//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓 var allCurrentUsernames = document.getElementsByClassName("username current"); //取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素 var selected = document.getElementById("myDiv").getElementsByClassName("selected");
document.activeElement:这个属性始终会引用 DOM 中当前获得了焦点的元素。
document.hasFocus():
var button = document.getElementById("myButton"); button.focus(); alert(document.activeElement === button); //true var button = document.getElementById("myButton"); button.focus(); alert(document.hasFocus()); //true
if (document.readyState == "complete"){ //执行操作 }
以上为 readyState 属性(loading complete)
document.charset
自定义数据属性:data-
innerHTML:读(子节点)、写 ,应避免频繁操作。
for (var i=0, len=values.length; i < len; i++){ ul.innerHTML += "<li>" + values[i] + "</li>"; //要避免这种频繁操作!! } //以下较为合理 var itemsHtml = ""; for (var i=0, len=values.length; i < len; i++){ itemsHtml += "<li>" + values[i] + "</li>"; } ul.innerHTML = itemsHtml;
outerHTML:读(包括调用它的元素及和所有子节点)、写
insertAdjacentHTML()
//作为前一个同辈元素插入
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>");
children:(childNodes类似,处理文本节点的空白符有差异);
contains():调用 contains() 方法的应该是祖先节点,
也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点,该方法返回 true ;否则,返回 false 。
innerText() outText()(不常用);
十二、DOM2和DOM3:
getComputedStyle()(非IE) currentStyle(IE);
元素大小:
1.偏移量:包括元素在屏幕上占用的所有可见的空间。
offsetHeight :元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)水平滚动条的高度、上边框高度和下边框高度。
offsetWidth :元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度。
offsetLeft :元素的左外边框至包含元素的左内边框之间的像素距离。
offsetTop :元素的上外边框至包含元素的上内边框之间的像素距离。
其中, offsetLeft 和 offsetTop 属性与包含元素有关,包含元素的引用保存在 offsetParent属性中。 offsetParent 属性不一定与 parentNode 的值相等。例如, <td> 元素的 offsetParent 是作为其祖先元素的 <table> 元素,因为 <table> 是在 DOM层次中距 <td> 最近的一个具有大小的元素。
2.客户区大小:指的是元素内容及其内边距所占据的空间大小。有关客户区大小的属性有两个: clientWidth 和 clientHeight 。其中, clientWidth 属性是元素内容区宽度加上左右内边距宽度; clientHeight 属性是元素内容区高度加上上下内边距高度。
最常用到这些属性的情况,就是像第 8 章讨论的确定浏览器视口大小的时候。如下面的例子所示,要确定浏览器视口大小,可以使用 document.documentElement 或 document.body (在 IE7 之前的版本中)的clientWidth 和 clientHeight 。
3.滚动大小:
scrollHeight :在没有滚动条的情况下,元素内容的总高度。
scrollWidth :在没有滚动条的情况下,元素内容的总宽度。
scrollLeft :被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollTop :被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
4.确定元素大小: getBoundingClientRect():这个方法返回会一个矩形对象,包含 4 个属性: left 、 top 、 right 和 bottom 。这些属性给出了元素在页面中相对于视口的位置。
遍历:
NodeIterator TreeWalker
范围:
createRange()......
十三:事件(文档或浏览器窗口中发生的一些特定的交互瞬间。)
1.事件流描述的是从页面中接收事件的顺序。
事件冒泡(IE):即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
事件捕获(Netscape):思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。(特殊时使用)
DOM事件流:“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。(IE9)
2.事件处理程序:响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以 "on" 开头.
a.HTML事件处理程序:会造成HTML和JS的紧密耦合;
b.DOM0级事件处理程序:
var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(this.id); //"myBtn" }; btn.onclick = null; //删除事件处理程序
c.DOM2级事件处理程序: addEventListener()和 removeEventListener() 。(true为捕获阶段调用,false为冒泡阶段调用)IE9+
var btn = document.getElementById("myBtn"); var handler = function(){ alert(this.id); }; btn.addEventListener("click", handler, false); // 这里省略了其他代码 btn.removeEventListener("click", handler, false); // 有效
以上代码表示如果第二个参数的匿名函数,则无法通过removeEventListener删除事件处理程序;
d.IE8-的事件处理程序: attachEvent() 和 detachEvent() 。this指向window
var btn = document.getElementById("myBtn"); btn.attachEvent("onclick", function(){ alert("Clicked"); });
e.跨浏览器的事件处理程序:
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type, handler, false); } else if (element.attachEvent){ element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } }; var btn = document.getElementById("myBtn"); var handler = function(){ alert("Clicked"); }; EventUtil.addHandler(btn, "click", handler); // 这里省略了其他代码 EventUtil.removeHandler(btn, "click", handler);
3.事件对象:
currentTarget :其事件处理程序当前正在处理事件的那个元素;
document.body.onclick = function(event){ alert(event.currentTarget === document.body); //true alert(this === document.body); //true alert(event.target === document.getElementById("myBtn")); //true };
当单击这个例子中的按钮时, this 和 currentTarget 都等于 document.body ,因为事件处理程序是注册到这个元素上的。然而, target 元素却等于按钮元素,因为它是 click 事件真正的目标。由于按钮上并没有注册事件处理程序,结果 click 事件就冒泡到了 document.body ,在那里事件才得到了处理。
在需要通过一个函数处理多个事件时,可以使用 type 属性。
var btn = document.getElementById("myBtn"); var handler = function(event){ switch(event.type){ case "click": alert("Clicked"); break; case "mouseover": event.target.style.backgroundColor = "red"; break; case "mouseout": event.target.style.backgroundColor = ""; break; } }; btn.onclick = handler; btn.onmouseover = handler; btn.onmouseout = handler;
preventDefault():组织特定事件的默认行为;(只有 cancelable 属性设置为 true 的事件,才可以使用.)
var link = document.getElementById("myLink"); link.onclick = function(event){ event.preventDefault(); };
stopPropagation():立即停止事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡。
var btn = document.getElementById("myBtn"); btn.onclick = function(event){ alert("Clicked"); event.stopPropagation(); }; document.body.onclick = function(event){ alert("Body clicked"); };
eventPhase:用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么 eventPhase 等于 1 ;如果事件处理程序处于目标对象上,则 event-Phase 等于 2 ;如果是在冒泡阶段调用的事件处理程序, eventPhase 等于 3 。
IE的事件对象:
returnValue 属性相当于 DOM中的 preventDefault() 方法; cancelBubble 属性与 DOM 中的 stopPropagation() 方法作用相同;
跨浏览器的事件对象:
var EventUtil = { addHandler: function(element, type, handler){ //省略的代码 }, getEvent: function(event){ return event ? event : window.event; }, getTarget: function(event){ return event.target || event.srcElement; }, preventDefault: function(event){ if (event.preventDefault){ event.preventDefault(); } else { event.returnValue = false; } }, removeHandler: function(element, type, handler){ //省略的代码 }, stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; } } };
4.事件类型:
a.UI事件:指的是那些不一定与用户操作有关的事件;
load:当页面完全加载后在 window 上面触发; unload :当页面完全卸载后在 window 上面触发;
abort :在用户停止下载过程时,如果嵌入的内容没有加载完,则在 <object> 元素上面触发。
error :当发生 JavaScript 错误时在 window 上面触发,当无法加载图像时在 <img> 元素上面触发,当无法加载嵌入内容时在 <object> 元素上面触发;
select :当用户选择文本框( <input> 或 <texterea> )中的一或多个字符时触发。
resize :当窗口或框架的大小变化时在 window 或框架上面触发。浏览器窗口最小化或最大化时也会触发 resize 事件。
scroll :当用户滚动带滚动条的元素中的内容时,在该元素上面触发。 <body> 元素中包含所加载页面的滚动条。
b.焦点事件:
blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
focus :在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
focusin :在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。
focusout :在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。
c.鼠标与滚轮事件:
click :在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。
dblclick :在用户双击主鼠标按钮(一般是左边的按钮)时触发。
mousedown :在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
mouseenter :在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
mouseleave :在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
mousemove :当鼠标指针在元素内部移动时重复地触发。
mouseout :在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。
mouseover :在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
mouseup :在用户释放鼠标按钮时触发。
客户区坐标位置:
鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的 clientX 和clientY 属性中。所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。
页面坐标位置:
页面坐标通过事件对象的 pageX 和pageY 属性,能告诉你事件是在页面中的什么位置发生的。这两个属性表示鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的。
在页面没有滚动的情况下, pageX 和 pageY 的值与 clientX 和 clientY 的值相等。
屏幕坐标位置:
鼠标事件发生时,不仅会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置。而通过 screenX 和 screenY 属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
修改键:
虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是 Shift、Ctrl、Alt 和 Meta(在 Windows键盘中是 Windows键,在苹果机中是 Cmd 键),它们经常被用来修改鼠标事件的行为。DOM 为此规定了 4 个属性,表示这些修改键的状态: shiftKey 、 ctrlKey 、 altKey 和 metaKey 。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为 true ,否则值为 false 。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户是否同时按下了其中的键。
相关元素:
event 对象的 relatedTarget 属性提供了相关元素的信息。这个属性只对于 mouseover和 mouseout 事件才包含值;对于其他事件,这个属性的值是 null 。
鼠标按钮:
0.左键 1滚轮键 2右键
事件信息:
event 对象中还提供了 detail 属性,用于给出有关事件的更多信息。对于鼠标事件来说, detail 中包含了一个数值,表示在给定位置上发生了多少次单击。在同一个元素上相继地发生一次 mousedown 和 mouseup 事件算作一次单击。 detail 属性从 1 开始计数,每次单击发生后都会递增。
鼠标滚轮事件:
mousewheel 事件,会冒泡。 wheelDelta 属性。当用户向前滚动鼠标滚轮时, wheelDelta 是 120 的倍数;当用户向后滚动鼠标滚轮时, wheelDelta 是-120 的倍数。
跨浏览器的实现:。。。
触摸设备。。。
d.键盘与文本事件:
keydown :当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
keypress :当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。按下 Esc 键也会触发这个事件。
keyup :当用户释放键盘上的键时触发。
键码:在发生 keydown 和 keyup 事件时, event 对象的 keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。
e.变动事件:。。。
HTML5事件:
1.contextmenu 事件:提供自定义上下文菜单;
2.beforeunload 事件:可以用作询问用户是否真的要关闭页面,还是希望继续留下来;
EventUtil.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; });
3. DOMContentLoaded 事件:在形成完整的 DOM树之后就会触发,不理会图像、JavaScript 文件、CSS 文件或其他资源是否已经下载完毕。
与 load 事件不同,DOMContentLoaded 支持在页面下载的早期添加事件处理程序,这也就意味着用户能够尽早地与页面进行交互。
4.readystatechange 事件:目的是提供与文档或元素的加载状态有关的信息;
5.pageshow 和 pagehide 事件
6.hashchange事件:在 URL 的参数列表(及 URL 中“#”号后面的所有字符串)发生变化时通知开发人员。
设备事件:。。。
5.内存和性能:添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个
函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的 DOM访问次数,会延迟整个页面的交互就绪时间。事实上,从如何利用好事件处理程序的角度出发,还是有一些方法能够提升性能的。
a.事件委托:利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
var list = document.getElementById("myLinks"); EventUtil.addHandler(list, "click", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "doSomething": document.title = "I changed the document's title"; break; case "goSomewhere": location.href = "http://www.wrox.com"; break; case "sayHi": alert("hi"); break; } });
如果可行的话,也可以考虑为 document 对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。
b.移除事件处理程序:如果你知道某个元素即将被移除,那么最好手工移除事件处理程序。
<div id="myDiv"> <input type="button" value="Click Me" id="myBtn"> </div>
----------------------------------------------------- var btn = document.getElementById("myBtn"); btn.onclick = function(){ //先执行某些操作 btn.onclick = null; // 移除事件处理程序 document.getElementById("myDiv").innerHTML = "Processing..."; };
只要是通过 onload 事件处理程序添加的东西,最后都要通过 onunload 事件处理程序将它们移除。
6.模拟事件: createEvent() 方法创建 event 对象(鼠标,键盘,其他,自定义)
十四、表单脚本:
在 JavaScript 中,表单对应的则是 HTMLForm-Element 类型。 HTMLFormElement 继承了 HTMLElement ,因而与其他 HTML 元素具有相同的默认属性。
form.submit():不会触发 submit 事件,因此要记得在调用此方法之前先验证表单数据。
form.reset():与调用 submit() 方法不同,调用 reset() 方法会像单击重置按钮一样触发 reset 事件。
var form = document.getElementById("form1"); //取得表单中的第一个字段 var field1 = form.elements[0]; //取得名为"textbox1"的字段 var field2 = form.elements["textbox1"]; //取得表单中包含的字段的数量 var fieldCount = form.elements.length;
var colorFields = form.elements["color"]:可以通过name属性返回一个NodeList;
除了 form 属性之外,可以通过 JavaScript 动态修改其他任何属性。
var form = document.getElementById("myForm"); var field = form.elements[0]; //修改 value 属性 field.value = "Another value"; //检查 form 属性的值 alert(field.form === form); //true //把焦点设置到当前字段 field.focus(); //禁用当前字段 field.disabled = true;
在第一次单击后就禁用提交按钮。(防止信用卡消费时等操作导致费用翻番):
EventUtil.addHandler(form, "submit", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); //取得提交按钮 var btn = target.elements["submit-btn"]; //禁用它 btn.disabled = true; })
每个表单字段都有两个方法: focus() 和 blur() 。HTML5 为表单字段新增了一个 autofocus 属性。
2.文本框脚本:
text和textarea支持select()方法,选择文本框中的所有文本;
取得选择的文本:
function getSelectedText(textbox){ return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd); }
选择部分文本:
textbox.value = "Hello world!" //选择所有文本 textbox.setSelectionRange(0, textbox.value.length); //"Hello world!" //选择前 3 个字符 textbox.setSelectionRange(0, 3); //"Hel" //选择第 4 到第 6 个字符 textbox.setSelectionRange(4, 7); //"o w
过滤输入;
操作剪贴板;
自动切换焦点;
HTML5验证API:required,email\url等,min max,pattren='正则' checkValidity() 方法可以检测表单中的某个字段是否有效。 novalidate;
3.选择框脚本;
4.表单序列化;
5.富文本编辑: contenteditable 属性
十五、Canvas
十六、HTML5脚本编程
postMessage()(跨文档消息传递)
原生拖放
媒体元素
历史状态管理:history.pushState() history.popState()
十七、错误处理与调试:
try { window.someNonexistentFunction(); } catch (error){ alert(error.message); }
finally 子句,则无论 try 或 catch 语句块中包含什么代码——甚至 return 语句,都不会阻止 finally 子句的执行。
错误类型:Error:基类型; EvalError; RangeError; ReferenceError(找不到对象);SyntaxError; TypeError(变量的类型并不符合要求所致); URIError。
在用大型JS库的函数时,可以使用try-catch语句。
function process(values){ if (!(values instanceof Array)){ throw new Error("process(): Argument must be an array."); } values.sort(); for (var i=0, len=values.length; i < len; i++){ if (values[i] > 100){ return values[i]; } } return -1; }
常见的错误类型:类型转换错误,数据类型错误,通信错误
把错误记录到服务器:
function logError(sev, msg){ var img = new Image(); img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" + encodeURIComponent(msg); }
十八、JS与XML
十九、E4X
二十、JSON
简单值:字符串用双引号;
对象:没有变量,属性要加双引号,末尾没有分号;
数组:没有变量和分号;
JSON对象的方法:
stringify():把JavaScript 对象序列化为 JSON 字符串;所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined 的任何属性也都会被跳过。还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化 JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进。
可以给对象定义 toJSON() 方法,返回其自身的 JSON 数据格式。(优先级高)
parse():把 JSON 字符串解析为原生 JavaScript 值;也接受第二个参数:
var book = { "title": "Professional JavaScript", "authors": [ "Nicholas C. Zakas" ], edition: 3, year: 2011, releaseDate: new Date(2011, 11, 1) }; var jsonText = JSON.stringify(book); var bookCopy = JSON.parse(jsonText, function(key, value){ if (key == "releaseDate"){ return new Date(value); } else { return value; } }); alert(bookCopy.releaseDate.getFullYear());
二十一、Ajax(Asynchronous JavaScript + XML)和Comet
function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string"){ var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i, len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex){ //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } } var xhr = createXHR();
xhr.open("get", "example.txt", false);
xhr.send(null):参数为作为请求主体发送的数据;
在收到响应后,响应的数据会自动填充 XHR 对象的属性,
responseText :作为响应主体被返回的文本。
responseXML;
status :响应的 HTTP 状态。(200成功,304未更新,404未找到资源)
statusText :HTTP 状态的说明。
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
发送异步请求,才能让JavaScript 继续执行而不必等待响应。此时,可以检测 XHR 对象的 readyState 属性,该属性表示请求
/响应过程的当前活动阶段。(01234 4为已完成)
HTTP头部信息:使用 setRequestHeader() 方法可以设置自定义的请求头部信息。
FormData:为序列化表单以及创建与表单格式相同的数据(用于通过 XHR 传输)提供了便利。
var data = new FormData(); data.append("name", "Nicholas"); .... xhr.send(data)
跨源资源共享(CORS):
Origin: http://www.nczonline.net ---------------------------------------------------------------- Access-Control-Allow-Origin: http://www.nczonline.net
其他跨域技术:
图像Ping: 最常用于跟踪用户点击页面或动态广告曝光次数。只能GET。
JSONP:动态 <script> 元素来使用的,使用时可以为src 属性指定一个跨域 URL。
function handleResponse(response){ alert("You’re at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name); } var script = document.createElement("script"); script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
var img = new Image(); img.onload = img.onerror = function(){ alert("Done!"); }; img.src = "http://www.example.com/test?name=Nicholas";
Comet:(服务器推送)
长轮询:页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据之后,浏览器关闭连接,随即又发起一个到服务器的新请求。这一过程在页面打开期间一直持续不断。使用 XHR 对象和 setTimeout() 就能实现。
HTTP 流:通过侦听 readystatechange 事件及检测 readyState的值是否为 3,就可以利用 XHR 对象实现 HTTP 流。
服务器发送事件:(SSE)Server-Sent Event,单向通信时推荐如读取比赛成绩
Web Sockets:聊天室等 双向通信时推荐;
二十二、JS高级技巧
1.高级函数
a.安全的类型检测:
function isFunction(value){ return Object.prototype.toString.call(value) == "[object Function]"; } function isRegExp(value){ return Object.prototype.toString.call(value) == "[object RegExp]"; }
b.作用域安全的构造函数:(防止没有new)
function Person(name, age, job){ if (this instanceof Person){ this.name = name; this.age = age; this.job = job; } else { return new Person(name, age, job); } } var person1 = Person("Nicholas", 29, "Software Engineer"); alert(window.name); //"" alert(person1.name); //"Nicholas" var person2 = new Person("Shelby", 34, "Ergonomist"); alert(person2.name); //"Shelby"
c.惰性载入函数:
惰性载入表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式,第一种就是在函数被调用时再处理函数。
在这个惰性载入的 createXHR() 中, if 语句的每一个分支都会为 createXHR 变量赋值,有效覆盖了原有的函数。最后一步便是调用新赋的函数。下一次调用 createXHR() 的时候,就会直接调用被分配的函数,这样就不用再次执行 if 语句了。
第二种方式return function(){}
d.函数绑定bind;
e.函数柯里化:调用另一个函数并为它传入要柯里化的函数和必要参数。用于创建已经设置好了一个或多个参数的函数。
2.防篡改对象:
a.不可扩展对象:
Object.preventExtensions() 方法可以改变这个行为,让你不能再给对象添加属性和方法。
var person = { name: "Nicholas" }; Object.preventExtensions(person); person.age = 29; alert(person.age); //undefined
Object.istExtensible() 方法还可以确定对象是否可以扩展。
b.密封的对象:密封对象不可扩展,而且已有成员的 [[Configurable]] 特性将被设置为 false 。
var person = { name: "Nicholas" }; Object.seal(person); person.age = 29; alert(person.age); //undefined delete person.name; alert(person.name); //"Nicholas"
Object.isExtensible()
c.冻结的对象:冻结的对象既不可扩展,又是密封的,而且对象数据属性的 [[Writable]] 特性会被设置为 false 。
var 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
Object.isFrozen() 方法用于检测冻结对象。
3.高级定时器:
重复定时器:
setTimeout(function(){ var div = document.getElementById("myDiv"); left = parseInt(div.style.left) + 5; div.style.left = left + "px"; if (left < 200){ setTimeout(arguments.callee, 50); } }, 50);
在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。
数组分块:
function chunk(array, process, context){ setTimeout(function(){ var item = array.shift(); process.call(context, item); if (array.length > 0){ setTimeout(arguments.callee, 100); } }, 100); }
chunk() 方法接受三个参数:要处理的项目的数组,用于处理项目的函数,以及可选的运行该函数的环境。
函数节流:
var processor = { timeoutId: null, //实际进行处理的方法 performProcessing: function(){ //实际执行的代码 }, //初始处理调用的方法 process: function(){ clearTimeout(this.timeoutId); var that = this; this.timeoutId = setTimeout(function(){ that.performProcessing(); }, 100); } }; //尝试开始执行 processor.process();
在这段代码中,创建了一个叫做 processor 对象。这个对象还有 2 个方法: process() 和performProcessing() 。前者是初始化任何处理所必须调用的,后者则实际进行应完成的处理。当调用了 process() ,第一步是清除存好的 timeoutId ,来阻止之前的调用被执行。然后,创建一个新的
定时器调用 performProcessing() 。时间间隔设为了 100ms,这表示最后一次调用 process() 之后至少 100ms 后才会调用 perform-Processing() 。所以如果 100ms之内调用了 process() 共 20 次, performanceProcessing() 仍只会被调用一次。
4.自定义事件:有助于解耦相关对象,保持功能的隔绝。
二十三、离线应用与客户端存储:
离线检测: navigator.onLine
应用缓存;
数据存储: 在 JavaScript 中通过 document.cookie 可以访问 cookie。 sessionStorage 和 localStorage 。前者严格用于在一个浏览器会话中存储数据,因为数据在浏览器关闭后会立即删除;后者用于跨会话持久化数据并遵循跨域安全策略。
二十四、最佳实践:
缩进大小为 4 个空格常见、合理的注释、变量和函数命名应规范、变量类型透明(当定义了一个变量后,它应该被初始化为一个值,来暗示它将来应该如何应用。或者用匈牙利标记法 "o" 代表对象, "s" 代表字符串, "i"代表整数, "f" 代表浮点数, "b" 代表布尔型。)、松散耦合、避免全局量。
命名空间:
//为 Professional Ajax 创建命名空间 Wrox.ProAjax = {}; //附加该书中所使用的其他对象 Wrox.ProAjax.EventUtil = { ... }; Wrox.ProAjax.CookieUtil = { ... }; //ProJS 还可以继续分别访问 Wrox.ProJS.EventUtil.addHandler( ... ); //以及 ProAjax Wrox.ProAjax.EventUtil.addHandler( ... );
避免与null比较(比如可以if (values instanceof Array){})
性能:
1、避免全局查找
function updateUI(){ var doc = document; var imgs = doc.getElementsByTagName("img"); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = doc.title + " image " + i; } var msg = doc.getElementById("msg"); msg.innerHTML = "Update complete."; }
将在一个函数中会用到多次的全局对象存储为局部变量总是没错的。
2.避免 with 语句
3.优化循环
对于大的 DOM 更改,使用 inner HTML 要比使用标准 DOM 方法创建同样的 DOM 结构快得多。
最小化现场更新
HTMLCollection 对象:
发生以下情况时会返回 HTMLCollection 对象:
1.进行了对 getElementsByTagName() 的调用;
2.获取了元素的 childNodes 属性;
3.获取了元素的 attributes 属性;
4.访问了特殊的集合,如 document.forms 、 document.images 等。
var images = document.getElementsByTagName("img"), image, i, len; for (i=0, len=images.length; i < len; i++){ image = images[i]; //处理 }
这段代码添加了 image 变量,保存了当前的图像。这之后,在循环内就没有理由再访问 images 的HTMLCollection 了 。
DOM 交互开销很大,所以需要限制 DOM 操作的次数。