JavaScript中的OOP
ECMAScript对象的定义:“无序属性的集合,其属性可以包含基本值,对象和函数。”
1. 属性的类型:定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。
1.1. 数据属性:包含一个数据值的位置
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特征,或者吧属性修改为访问器属性。默认为true
var person = {};
Object.defineProperty(person,”name”,{
configurable :false,
value : “xiao”
});
alert(person.name); //xiao
delete person.name;
alert(person.name); //xiao
一旦把属性定义为不可配置的,就不能再把它变回可配置。
[[Enumerable]]:表示能否通过for-in循环属性。默认为true。
[[Writeable]]:表示能否修改属性的值。默认为true。
[[Value]]:包含这个属性的数据值。默认值是undefined。
1.2. 访问器属性:包含一对getter和setter函数。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特征,或者能否把属性修改为数据属性。默认值为true。
[[Enumerable]]:表示能否通过for-in循环返回属性,这个默认值是true。
[[Get]]:在读取属性时调用的函数。默认值为undefined。
[[Set]]:在写入属性时调用的函数。默认值为undefined。
var book = {
_year: 2004,
edition: 1
};
Object.definedProperty(book,”year”,{
get:function{
returnthis._year;
},
set:function{
if (newValue > 2004){
this.year = newValue;
this.edition += newValue –2004;
}
}
});
book.year = 2005;
alert(book.edition);
2. 创建对象
2.1.工厂模式
functioncreatePerson(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(“xiao”,29,”wulong”);
var person2 =createPerson(“ling”,34,”wulong”);
解决了对象创建问题,但是却没有解决对象识别问题,即创建的对象是哪个类型。
2.2.构造函数模式
functionPerson(name.age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 =new Person(“xiao”,32,”wulong”);
var person2 =new Person(“ling”,21,”wulong’);
与工厂模式对比:1.没有显示的创建对象2.没有将属性和方法赋给this对象3.没有return语句。
构造函数名首写字母应该大写。
可以将它的标示符为一种特定的类型。
每个方法都要在每个实例上重新创建一遍
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function(“alert(this.name)”); //与声明函数在逻辑上是等价的
}
var person1 = new Person(“xiao”,21,”wulong’);
var person2 = new Person(“ling”,22,”wulong”);
alert(person1.syaName ==person2.sayName); //false
function Person(name,age,job){
this.name= name;
this.age=age;
this.job= job;
this.sayName= syaName;
}
function sayName(){
alert(this.name);
}
var person1 = new Person(“xiao”,23,”wulong”);
var person2 = new person(“ling”,21,”wulong”);
2.3.原型模式
每个函数都有一个proptotype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
function Person(){}
Person.proptotype.name = “xiao”;
Person.proptotype.age = 28;
Person.proptotype.job = “wulong”;
Person.proptotype.sayName = function(){
alert(this.name);
};
var peron1 = new Person();
person1.sayName(); //xiao
var person2 = new Person();
person2.sayName();
alert(person1.sayName ==person2.sayName()) //true
理解如下图所示:
创建一个新函数,就会为该函数创建一个proptotype属性,这个属性指向原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向proptotype属性所在函数指针。当调用构造函数创建一个新对象,该对象的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262中这个指针叫[[Proptotype]]。但在Firefox、Safari、Chrome在每个对象支持一个属性_propto_;这个属性对脚本饿是完全不可见的。这个链接存在于实例对象与构造函数原型兑现之间,而不是存在实例对象与构造函数之间。
可以通过isProptotypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Proptotype]]指向调用isProptotype()方法的对象(Person.proptotype),那么这个方法返回true。
Object.getProptotypeOf():返回[[Proptotype]]的值。
alert(Object.getProptotypeOf(person1)== Person.proptotype);
如果该实例属性添加一个与原型对象相同名称的属行,访问得到的是实例属性,会屏蔽原型中的那个属性。
使用hasOwnProptotype()方法可以检测一个属性是存在实例中,还是存在原型中。只给定属性存在于对象实例中时,才返回true。
functionPerson(){
}
Person.prototype.name= “xiao”;
Person.prototype.age= 29;
Person.prototype.sayName= function(){
alert(this.name);
};
varperson1 = new Person();
varperson2 = new Person();
alert(person1.hasOwnProperty(“name”)); //false;
person1.name= “xiao”;
alert(person1.hasOwnProperty(“name”)); //true
2.3.1.原型与in操作符:无论该属性是存在实例或者原型中,都返回true。
functionPerson(){
}
Person.prototype.name= “xiao”;
Person.prototype.age= 25;
Person.prototype.sayName= function(){
alert(this.name);
}
varperson1 = new Person();
varperson2 = new Person();
alert(person1.hasOwnproperty(“name”)); //false
alert(“name”in person1); //true
通过该方法就可以判断属性是来自原型还是实例:
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&& (name in object);
}
2.3.3.更简洁的原型语法
functionPerson(){
}
Person.prototype= {
name : “xiao”,
age : 29,
sayName : function(){
alert(this.name);
}
};
将Person.prototype重新设置为等于一个以对象字面量形式创建的新对象。原型对象中的constructor属性不再指向Person了。需要强制指向。
constructor: Person;
2.3.4.原型的动态性
2.3.5.原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的应用类型,都采用这种模式创建的。
String.prototype.startwith= function(){
return this.indexof(text) == 0;
}
2.3.6.原生对象的问题
它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值,原型最大的问题在于共享本质所导致的。
function Person(){
}
Person.prototype = {
constructor : Person,
name : “xiao”,
age : 32,
job : “wulong”,
friends : [“ling”,”ling”],
sayName : function(){
alert(this.name);
}
}
var person1 = newPerson();
var person2 = newPerson();
person1.friends.push(“wo”);
alert(person1.friends); //ling,ling,wo
alert(person2.friends); //ling,ling,wo
2.3.7.组合使用构造函数和原型模式
构造函数用于定义实例的属性。
原型模式用于定义实例的方法
functionPerson(name,age){
this.name = name;
this.age = age;
}
Person.prototype= {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
2.3.8.动态原型模式
动态原型模式,它把所有的信息都封装在构造函数中,在通过构造函数初始化原型(仅在必要情况下),有保持了同时使用构造函数和原型的优点。
functionPerson(name,age){
this.name = name;
this.age = age;
if(typeof this.sayName = “function”){
Person.prototype.sayName= function(){
alert(this.name);
}
}
}
2.3.9.寄生构造函数模式
functionPerson(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
return obj;
}
functionSpecialArray(){
var values = new Array();
values.push.apply(values,arguments);
values.toPipedString = function(){
return this.join(“|”);
}
return values;
}
varcolors = new SpecialArray(“red”,”blue”,”green”);
alert(colors.toPipedString()); //red|blue|green
3. 继承
3.1.原型链
functionSuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue= function(){
return this.property;
}
functionSubType(){
this.Subproperty = false;
}
//继承了SuperType
SubType.prototype= new SuperType();
SubType.protptype.getSubValue= function(){
return this.subprototype;
}
var instance = newSubType();
alert(instance.getSuperValue); //true
理解下图:
每个构造函数都有一个原型对象,原型对象都包含一个构造函数的指针,而实例都包含一个指向原型对象的内部指针。假如让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也 包含着一个指向另一个构造函数的指针。。。。。。
图中SubType中有一个内部指针指向SubType的原型对象,原型对象中有个一[[prototype]]内部指针指向SuperType的原型对象。最终就是:instance指向SubType的原型,SubType的原型指向SuperType的原型对象。prototype是实例方法,而getSuperValue是原型方法,所以prototype存在于SubType的原型对象中,instance.constructor指向SubType,而SubType的原型指向了另一个对象SuperType的原型,而这个原型对象的constructor属性指向的是SuperType。
3.1.1.默认原型
如图所示:
JavaScript对象模型: