"红砖"JS高程笔记P5

来源:互联网 发布:中国移动 人工智能 编辑:程序博客网 时间:2024/04/28 12:11
对象是一组无特定顺序的值,对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.
可以把ECMAScript的对象想象成散列表,就是一组名值对,其中值可以是数据或函数.
每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是自定义的类型.




理解对象
用字面量成为创建对象:
var person = {
name : "Nicholas",
age : 29,
job : "Softwars Engineer",


sayName : function(){
alert(this.name);
}
};
这些属性在创建时都带有一些特征值(characteristic),Javascript通过这些特征值来定义它们的行为.




属性类型
ECMAScript在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征.
定义这些特性是为了实现Javascript引擎用的,因此在Javascript中不能直接访问它们.
为了表示特性是内部值,该规范把它们放在了两对方括号中,如[[Enumerable]].
ECMAScript中有两种属性 : 数据属性和访问器属性.
1.数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值.
数据属性有4个描述其行为的特性:
a.[[Configurable]] : 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,
直接在对象上定义的属性,它们这个特性的默认值是true.
b.[[Enumerable]] : 表示能否通过for-in循环返回属性,直接在对象上定义的属性,它们这个特性的默认值是true.
c.[[Writable]] : 表示能否修改属性的值,直接在对象上定义的属性,它们这个特性的默认值是true.
d.[[Value]] : 包含这个属性的数据值,读取属性值的时候,从这个位置读,写入属性值的时候,把新值保存在这个位置,这个特性的默认值为true.
要修改属性默认的特性,必须使用ECMAScript的Object.defineProperty()方法.
这个方法接收三个参数 : 属性所在的对象,属性的名字和一个描述符对象.
其中,描述符(descriptor)对象的属性必须是:configurable,enumerable,writable和value,
设置其中的一或多个值,可以修改对应的特性值,如:
var person = {};
Object.defineProperty(person,"name",{
writable : false,
value : 'Nicholas'
});


alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
注意:一旦把属性configurable定义为不可配置(false)的,就不能再把它变回可配置(true)了.
注意:在调用Object.defineProperty()方法时(即用此方法创建的属性),如果不指定,configurable,enumerable和writable特性的默认值都是false.
2.访问器属性
访问器属性不包含数据值,它们包含一对getter和setter函数,但两个函数都不是必需的.
在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;
在写入访问器属性时,会调用setter函数并传入新值,函数负责决定如何处理数据.
访问器属性有如下4个特性:
a.[[Configurable]] : 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为数据属性,
直接在对象上定义的属性,它们这个特性的默认值是true.
b.[[Enumerable]] : 表示能否通过for-in循环返回属性,直接在对象上定义的属性,它们这个特性的默认值是true.
c.[[Get]] : 在读取属性时调用的函数,默认值为undefined.
d.[[Set]] : 在写入属性时调用的函数,默认值为undefined.
访问器属性不能直接定义,必须使用Object.defineProperty()来定义,如:
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
注意:_year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性.




定义多个属性
ECMAScript定义了一个Object.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;
}
}
}
});
以上代码在book对象上定义了两个数据属性(_year和edition)和一个访问器属性(year).




读取属性的特性
Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符.
这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称,返回值是一个对象.
var descriptor = Object.getOwnPropertyDescriptor(book,"year");
alert(descriptor.value); //undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); //"function"
在Javascript中,可以针对任何对象(包括DOM和BOM对象)使用Object.getOwnPropertyDescriptor()方法.




----------------------------------------------------------------------




创建对象
使用Object构造函数和对象字面量只可创建单个对象,
使用同一个接口创建多个对象,会产生大量重复代码,所以有了工厂模式.


工厂模式
工厂模式是一种设计模式,模式抽象了创建具体对象的过程,在ECMAScript中,用函数来封装以特定接口创建对象的细节,如:
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("Greg",27,"Doctor");
可以无数次调用createPerson函数,每次它都会返回一个包含三个属性一个方法的对象,
解决了创建多个相似对象产生冗余代码的问题,但无法识别工厂模式创建的对象的类型,所以有了构造函数模式.




构造函数模式
ECMAScript中的构造函数可用来创建特定类型的对象,
原生构造函数在运行时会自动出现在执行环境,此外,还可以创建自定义的构造函数,从而定义对象类型的属性和方法:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Greg",27,"Doctor");
调用构造函数实际上经历以下4个步骤:
a.创建一个新对象.
b.将构造函数的作用域赋给新对象(因此this就指向了这个新对象).
c.执行构造函数中的代码(为这个新对象添加属性).
d.返回新对象.
对象(person1)有一个constructor属性,该属性指向其构造函数(Person).
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型(引用类型).
自定义构造函数是定义在Global对象(浏览器中是window对象)中的.
1.将构造函数当做函数
构造函数与其他函数的唯一区别,就在于调用它们的方式不同.
任何函数,只要通过new操作符来调用,那它就可以作为构造函数,而任何构造函数,如果不通过new操作符来调用,就跟普通函数一样.
//当作构造函数使用
var person = new Person("Nicholas",29,"Software Engineer");
person.sayName();
//作为普通函数调用
Person("Greg",27,"Nurse"); //添加到window
window.sayName();
2.构造函数的问题
构造函数的缺陷是使用构造函数时,每个方法都要在每个实例上重新创建一遍.
ECMAScript中的函数是对象,因此每定义一个函数,也就实例化了一个对象.
不同实例上的同名函数是不相等的.
问题可通过原型模式来解决.




原型模式
创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,
而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.
prototype就是通过调用构造函数而创建的那个对象实例的原型对象.
function Person(){  }
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
1.理解原型对象
无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象.
在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针.
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法,则都是从Object继承而来的.
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(__proto__),指向构造函数的原型对象.
原型对象可以通过isPrototypeOf()方法来确定与参数对象之间是否存在原型关系,如:
alert(Person.prototype.isPrototypeOf(person1)); //true
使用Object.getPrototypeOf()方法会返回__proto__的值:
alert(Object.getPrototypeOf(person1) == Person.prototype);//true
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性,搜索首先从对象实例本身开始,
如果在实例中找到了具有给定名字的属性,则返回该属性的值,
如果没找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性.
原型最初只包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问.
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值,
如果我们在实例中添加一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性.
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,添加这个属性只会阻止我们访问原型中的那个属性,
但不会修改那个属性,即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接,
不过,使用delete操作符则可以完全删除实例属性,从而能够重新访问原型中的属性,如:
person1.name = "Greg"; //来自实例,屏蔽了原型的name属性Nicholas
alert(person1.name); //"Greg"
delete person1.name;
alert(person1.name); //"Nicholas" --- 来自原型
对象实例使用hasOwnProperty()方法可以检测一个属性是否存在实例中,方法(从Object继承而来)只在给定属性存在于对象实例中时,才会返回true.
注意:想在获得原型属性的描述符,就直接在原型对象上调用Object.getOwnPropertyDescriptor()方法.
2.原型与in操作符
有两种方式使用in操作符: 单独使用 和 在for-in循环中使用.
在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在与实例中还是原型中.
alert("name" in person2); //true
//以下函数能确定属性name是否存在原型中:
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
在使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性,
屏蔽了原型中不可枚举属性(即将[[Enumerable]]标记为false的属性)的实例属性也会在for-in循环中返回.
要取得对象上所有可枚举的实例属性,可以使用ECMAScript的Object.keys()方法,这个方法接收一个对象作为参数,
返回一个包含所有可枚举属性的字符串数组,如:
function Person(){  }
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys); //["name","age","job","sayName"]
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //["name","age"]
如果想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法:
var keys = Object.getOwnPropertyNames(Person.prototype);
3.更简单的原型语法
function Person(){ }
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function(){
alert(this.name);
}
};
Object.defineProperty(person.prototype,"constructor",{
enumerable : false,
value : Person
});
/*
以上代码,是为了避免定义prototype时多次输入,进而把原型封装为字面量,
但这种封装等于重写了prototype对象,也就覆盖了constructor属性,
所以需要把constructor属性回指,但默认的constructor属性的[[Enumerable]]特性是false,即自定义的constructor是true,
所以还有进行特性设置.
*/
4.原型的动态性
由于调用属性是一次从实例到原型的搜索过程,因此对原型对象所做的任何修改都能够立即从实例上反映出来,
即使是先创建了实例后再修改原型也如此.
在调用构造函数时会为实例添加一个指向最初原型的__proto__指针,
而当把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系.
重写原型对象就切断了现有原型与任何之前已经存在的对象实例之间的联系,而对象实例们引用的仍然是最初的原型.
/*既是说,重写原型必须在新建实例之前进行*/
5.原生对象的原型
所有原生引用类型都在其构造函数的原型上定义了方法.
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法,
可以像修改自定义对象的原型一样修改原生对象的原型.
String.prototype.startsWith = function(text){
return this.indexOf(text) == 0;
};
var mag = "Hello world!";
alert(msg.startsWith("Hello")); //true
6.原型对象的问题
原型模式的缺点: 
首先,它省略了为构造函数传递初始化参数步骤,使得所有实例在默认情况下都将取得相同的属性值,
其次,原型中所有属性都被实例共享,实例一般要有属于自己的全部属性的.
所以就有了组合使用构造函数模式和原型模式的解决方案.




组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性.
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




动态原型模式
动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在需要的情况下),
又同时使用构造函数和原型的优点,
可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型.
function Person(name,age,job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("Nicholas",29,"Software Engineer");
friend.sayName();
这里只在sayName()方法不存在的情况下,才会将它添加到原型中,
以上代码只会在初次调用构造函数时才会执行,而后,原型已经完成初始化.
使用动态原型模式时,不能使用对象字面量重写原型,因为如果在仅仅创建了实例的情况下重写原型,
那么就会切断现有实例与新原型之间的联系.




寄生构造函数模式
寄生(parasitic)构造函数模式除了使用new操作符并把使用的包装函数叫做构造函数之外,其他和工厂模式是一样的.
function Person(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 friend = new Person("Nicholas",29,"Software Engineer");
friend.sayName(); //"Nicholas"
构造函数在不返回值的情况下,默认会返回新对象实例,而通过在构造函数的末尾添加一个return语句,可以重写调用构造函数时返回的值.
/*寄生构造函数模式多用于创建具有额外方法的原生应用类型的构造函数*/
function SpecialArray(){
//创建数组
var value = new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
return this.join("|");
};
//返回数组
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString()); //"red|blue|green";
注意:返回的对象与构造函数和构造函数的原型属性之间没有任何关系,
即返回的对象不能依赖instanceof操作符来确定对象类型.




稳妥构造函数模式
稳妥对象(durable objects)指的是没有公共属性,而且其方法也不应用this的对象.
稳妥对象最适合在安全环境中(这些环境中会禁止使用this和new)使用.
/*不引用寄生构造函数模式中的this和不使用new调用构造函数即是*/
function Person(name){
var o = new Object();
o.sayName = function(){
alert(name);
};
return o;
}
var friend = Person("Movement");
friend.sayName(); //"Movement"
除了调用sayName()方法外,没有别的方式可以访问其数据成员,
即使给这个对象添加方法或数据成员,也不可能有别的方法访问传入到构造函数中的原始数据.




-----------------------------------------------------------------------------------------------------




继承
一般语言都支持两种继承方式:接口继承和实现继承.
接口继承只继承方法签名,实现继承继承实际的方法.
ECMAScript只支持实现继承,而其实现继承主要是依靠原型链来实现的.


原型链
原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.
若让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,
相应地,另一个原型中也包含着一个指向另一个构造函数的指针.
原型链的一种基本模式:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType(); //继承了SuperType
SubType.prototype.getSubValue = function(){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
/*原型对象方法中的this指向的是实例,因为实例继承了这个方法*/
最终的结果是:
instance指向SubType的原型,SubType的原型又指向SuperType的原型,property位于SubType.prototype中,
instance.constructor指向的是SuperType,SubType原型指向SuperType的原型,而这个原型对象的constructor属性指向SuperType.
1.默认的原型
所有函数的默认原型都是Object的实例,因此默认原型都会包含一个__proto__指针,指向Object.prototype,
这正是所有自定义类型都会继承默认方法的原因,实际上调用的默认方法(即toString和valueOf等)是保存在Object.prototype中的.
2.确定原型和实例的关系
可以通过两种方式来确定原型和实例之间的关系.
第一种是使用instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true.
第二种是使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,也会返回true.
alert(Object.prototype.isPrototypeOf(instance)); //true
3.谨慎地定义方法
子类型有时候需要覆盖超类型中的某个方法,或者需要添加超类型中不存在的某个方法,
但记住给原型添加方法的代码一定要放在替换原型的语句之后.
在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做会重写原型链:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType(); //继承了SuperType
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = {
getSubValue : function(){
return this.subproperty;
}
};
var instance = new new SubType();
alert(instance.getSuperValue()); //error
4.原型链的问题
原型链的问题是在通过原型来实现继承时,原型实际上会变成另一个类型的实例,
于是,原先的实例属性也就顺理成章地变成了现在的原型属性,而原型属性会被所有实例共享.
原型链的第二个问题是,在创建子类型的实例时,不能向超类型的构造函数中传递参数.




借用构造函数
借用构造函数(constructor stealing)的技术的基本思想是在子类型构造函数的内部调用超类型构造函数.
函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法可以在(之后)新创建的对象上执行构造函数.
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this);//继承了SuperType
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
通过使用call()方法,在之后新创建的SubType实例的环境下调用了SuperType构造函数,
这样一来就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,
结果,SubType的每个实例都会具有自己的colors属性的副本.
1.传递参数
借用构造函数可以在子类型构造函数中向超类型构造函数传递参数.
function SuperType(name){
this.name = name;
}
function SubType(){
SuperType.call(this,"Nicholas");
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //"Nicholas"
alert(instance.age); //29
2.借用构造函数的问题
方法都在构造函数中定义,因此函数复用就无从谈起,
且在超类型的原型中定义的方法,对子类型而言也是不可见.




组合继承
组合继承(combination inheritance),指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式.
背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承.
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
/*以上例子中是利用了当对象读取属性时是通过从实例内部开始搜索再到原型对象的方式实现不共享属性的*/




原型式继承
原型式继承(Prototypal Inherritance)的思路是借助原型基于已有的对象创建新对象,同时还不必创建自定义类型.
function object(o){
function F(){}
F.prototype = o;
return new F();
}
在object()函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,
最后返回这个临时类型的一个新实例.
从本质上讲,object()对传入其中的对象执行了一次浅复制.
/*浅复制指只对对象的字段值复制,字段引用的对象不复制*/
var person = {
name : "Nicholas",
friend : ["Shelby","Court","Van"]
};
var anotherPerson = Object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
作为另一个对象基础的是person对象,于是把它传入到object()函数中,然后该函数就会返回一个新对象,
这个新对象将person作为原型,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性,
这意味着person.friends不仅属于perosn所有,而且也会被anotherPerson以及yetAnotherPerson共享.
Object.create()方法规范化了原型式继承,方法接收两个参数:
一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象,
在传入一个参数的情况下,Object.create()与Object()方法的行为相同.
var person = {
name : "Nicholas",
friends : ["Shelby","Court","Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都通过自己的描述符定义,
以这种方式指定的任何属性都会覆盖原型对象上的同名属性.
var person = {
name : "Nicholas",
friends : ["Shelby","Court","Van"]
}
var anotherPerson = Object.create(person,{
name : {
value : "Greg"
}
});
alert(anotherPerson.name); //"Greg"
/*但缺点是原型式继承中包含的引用类型值的属性始终被共享*/


寄生式继承
寄生式(parasitic)继承的思路是创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,
最后再像真地是它做了所有工作一样返回对象.
var person = {
name : "Nicholas",
friends : ["Shelby","Court","Van"]
};
function createAnother(original){
var clone = object(original);//通过调用函数创建一个新对象
clone.sayHi = function(){//以某种方式来增强这个对象
alert("hi");
};
return clone;
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //Hi
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率.


寄生组合式继承
前面所说的组合继承的缺点是无论在任何情况下,都会调用两次超类型构造函数:
一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,
使得不得不在调用子类型构造函数时重写这些属性.
而寄生组合继承就是解决办法,寄生组合继承即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法.
本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型:
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype);//创建对象
prototpye.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);
};
原创粉丝点击