第六章 面向对象的程序设计
1、理解对象:无序属性的集合,其属性可以包含基本值、对象或者函数
1)属性类型:
数据属性:4个特性——[[Configurable]]表示能否通过delete删除属性从而重新定义属性,能否删除属性的特性,或者能否把属性修改为访问器属性,这个特性默认值为true;[[Enumerable]]表示能否通过for-in循环返回属性,e这个特性默认值为true;[[Writable]]表示能否修改属性的值,这个特性默认值为true;[[Value]]包含这个属性的数据值,读取属性值的时候从这个位置读,写入属性值的时候把新值保存在这个位置;要修改属性默认的特性,必须使用ECMAScript5的object.defineProperty()方法,接收三个参数——属性所在的对象、属性的名字和一个描述符对象。在调用 Object.defineProperty() 方法时,如果不指定, configurable 、 enumerable 和writable 特性的默认值都是 false 。
访问器属性:不包含数据值,包含一对getter和setter函数,在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据;4个特性——[[Configurable]]表示能否通过delete删除属性从而重新定义属性,能否删除属性的特性,或者能否把属性修改为访问器属性,这个特性默认值为true;[[Enumerable]]表示能否通过for-in循环返回属性,这个特性默认值为true;[[Get]]在读取属性时调用的函数,这个特性默认值为undefined;[[Set]]在写入属性时调用的函数,这个特性默认值为undefined;要定义属性默认的特性,必须使用ECMAScript5的object.defineProperty5()方法,接收三个参数——属性所在的对象、属性的名字和一个描述符对象
2)定义多个属性:ECMAScript5定义了object.defineProperties()方法用于定义多个属性,接收两个参数——要添加和修改其属性的对象和要添加和修改其属性的对象的属性,IE9+、Firefox4+、Safari5+、Opera12+、Chrome有效
3)读取属性的特性:ECMAScript5定义了object.getOwnPropertyDescriptor()方法用于取得给定属性的描述符,接收两个属性——属性所在的对象和要读取其描述符的属性名称
2、创建对象
1)工厂模式:用函数来封装以特定接口创建对象的细节
2)构造函数模式:
没有显式地创建对象;直接将属性和方法赋给了this对象;没有return语句
调用构造函数的4个步骤——创建一个新对象;将构造函数的作用域赋给新对象(this指向这个新对象);执行构造函数中的代码(为这个新对象添加);返回新对象
构造函数与其他函数的区别:构造函数应该以一个大写字母开头,非构造函数则应该以一个小写字母开头;构造函数通过new操作符来调用;构造函数的每个方法都要在每个实例上重新创建
3)原型模式:每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象包含可以由特定类型的所有实例共享的属性和方法。
isPrototypeOf() 方法:如果 [[Prototype]] 指向调用 isPrototypeOf() 方法的原型对象,那么这个方法就返回 true;
Object.getPrototypeOf()方法:返回 [[Prototype]] 的值,支持这个方法的浏览器有 IE9+、Firefox 3.5+、Safari 5+、Opera 12+和 Chrome;
hasOwnProperty() 方法:检测一个属性是存在于实例中,还是存在于原型中只在给定属性存在于对象实例中时,才会返回 true;
hasPrototypeProperty() 方法:检测一个属性是存在于实例中,还是存在于原型中只在给定属性存在于原型对象中时,才会返回 true;
in操作符:通过对象能够访问给定属性时返回 true ,无论该属性存在于实例中还是原型中;
Object.keys() 方法:接收一个对象作为参数,返回一个包含所有可枚举的实例属性;Object.getOwnPropertyNames()方法:得到所有实例属性,无论它是否可枚举;支持这两个方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera12+和 Chrome。
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值;
4)组合使用构造函数模式和原型模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
5)动态原型模式:把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。
6)寄生构造函数模式:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;返回的对象与构造函数或者与构造函数的原型属性之间没有关系;构造函数返回的对象与在构造函数外部创建的对象没有什么不同,不能依赖 instanceof 操作符来确定对象类型。
7)稳妥构造函数模式:稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用 this ;二是不使用 new 操作符调用构造函数。
3、继承:ECMAScript只支持实现继承
1)原型链:给原型添加方法的代码一定要放在替换原型的语句之后;在通过原型链实现继承时,不能使用对象字面量创建原型方法;在通过原型来实现继承时,原型实际上会变成另一个类型的实例;在创建子类型的实例时,不能向超类型的构造函数中传递参数。
2)借用构造函数:在子类型构造函数的内部调用超类型构造函数,通过使用 apply() 和 call() 方法也可以在新创建的对象上执行构造函数;无法实现函数复用。
3)组合继承:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
4)原型式继承:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
5)寄生式继承:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回该对象。
function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
6)寄生组合式继承:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
第七章 函数表达式
函数声明:function functionName(arg0,arg1,arg2){//函数体},在执行代码之前会先读取函数声明
函数表达式:var functionName=function(arg0,arg1,arg2){//函数体},这个情况下创建的函数叫做匿名函数(拉姆达函数)
1、递归:
arguments.callee是一个指向正在执行的函数的指针,因此可以用它实现对函数的递归调用
function factorial(num){
f(num <= 1){
return1;
}else{
return num*arguments.callee(num-1);
}
}
在严格模式下,不能通过脚本访问arguments.callee
var factorial=(function f(num){
f(num <= 1){
return1;
}else{
return num*f(num-1);
}
});
2、闭包:有权访问另一个函数作用域中的变量的函数;创建闭包的常见方式,就是在一个函数内部创建另一个函数
1)闭包与变量:闭包只能取得包含函数中任何变量的最后一个值。解决方法如下:
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
3、模仿块级作用域:
块级作用域(私有作用域)的匿名函数:
(function(){
//这里是块级作用域
})();
4、私有变量:包括函数的参数、局部变量和在函数内部定义的其他函数;特权方法:有权访问私有变量和私有函数的公有方法;
在构造函数中定义特权方法:
function MyObject(){
//私有变量和私有函数
var privateVariable=10;
Function privateFunction(){
return false;
}
//特权方法
this.publicMethod=function(){
privateVariable++;
return privateFunction();
};
}
静态私有变量:通过在私有作用域中为自定义类型定义私有变量或函数以及特权方法,这个模式与在构造函数中定义特权方法的主要区别,就在于私有变量和函数是由实例共享的。其基本模式如下:
(function(){
//私有变量和私有函数
var privateVariable=10;
function privateFunction(){
return false;
}
//构造函数
MyObject=function(){
};
//公有/特权方法
MyObject.prototype.publicMethod=function(){
privateVariable++;
return privateFunction();
};
})();
模块模式:为单例创建私有变量和特权方法;单例:指的就是只有一个实例的对象,JavaScript是以对象字面量的方式来创建单例对象。其基本模式如下:
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;
publicMethod=function(){
privateVariable++;
return privateFunction();
};
//返回这个对象
return object;
}();