function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承SuperType
SubType.prototype = new SuperType();
//给子类添加方法,
//一定要放在SubType.prototype = new SuperType();语句之后
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//实例化一个对象,具有SubType和SuperType的属性和方法
var instance = new SubType();
alert(instance.getSuperValue()); //true
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
代码分析:
把 SuperType
的实例当成SubType
的原型,这样新原型就具有SuperType
的所有属性和方法。
getSuperValue()
方法仍然在superType.prototype
中,但property
则位于SubType.prototype
中,为什么?
因为property
是实例属性,而getSuperValue()
是一个原型方法。SubType.prototype
是SuperType
的实例,所以property
就位于该实例中了。
为什么instance
的constructor
指向的是SuperType
?
因为SubType
的原型指向了另一个对象SuperType
的原型,而这个原型对象的constructor
指向SuperType
。
一、包含引用类型值。
SuperType
有一个引用类型值的属性colors
,给SubType.prototype
赋值new SuperType()
,相当于给SubType.prototype
添加了colors
属性,那么使用SubType
创建的实例就会共享原型属性,就会造成其中一个实例对其进行修改,另一个实例也会受到影响。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承SuperType
SubType.prototype = new 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,black"
二、没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
借用构造函数(很少单独使用)思想:在子类型构造函数的内部调用超类型的构造函数。
依旧上代码:
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//instance property
this.age = 29;
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.name); //"Nicholas";
alert(instance1.age); //29
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
分析:
使用call()
或apply()
,可以在将来新创建的SubType
实例的环境下调用SuperType
构造函数,这样就会在新SubType
对象上执行SuperType()
函数中定义的所有对象初始化代码。SubType
的每个实例就都会有自己的实例属性副本了。
SuperType还可以接收参数
注意:在调用超类型构造函数后,再添加应该在子类型中定义的属性。
借用构造函数的问题: 方法都在构造函数中定义,函数复用就无从谈起了。
组合继承(最常用的继承模式)可以看如何创建一个对象–组合使用构造函数模式和原型模式。
文章里有说:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。这样每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度的节省了内存。还支持传递参数。
上代码:
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);//第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType();//第一次调用SuperType()
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
分析:
第一次调用SuperType()
,SubType.prototype
会得到两个属性:name
和colors
。
当调用SubType
构造函数时会第二次调用SuperType
构造函数,这一次又在新对象上创建了实例属性name
和colors
。
这样导致有两组name
和colors
属性,一组在SubType
原型中,一组在实例上。解决办法采用寄生组合式继承。
属性在构造方法SuperType
里定义,方法定义在原型SuperType.prototype
上,这样不同实例instance1
和instance2
既分别有自己的属性,又可以使用相同的方法。
道格拉斯·克罗克福德在2006年写过一篇文章,叫做JavaScript中的原型式继承。他的想法是借住原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
//道格拉斯·克罗克福德给出的object函数
function object(o){
function F(){}
//将传进来的参数作为原型的值
F.prototype = o;
return new F();
}
//已有对象
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
//person作为原型的值,创造出anotherPerson实例,具有name和friends属性
//实际上相当于创建了一个person的副本
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
console.log(anotherPerson.name);//Greg
console.log(anotherPerson.friends);//["Shelby", "Court", "Van", "Rob"]
console.log(person.friends);//["Shelby", "Court", "Van", "Rob"]
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(anotherPerson.friends);//["Shelby", "Court", "Van", "Rob","Barbie"]
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
person
作为原型的值,创造出anotherPerson
和yetAnotherPerson
实例,具有name
和friends
属性, 实际上相当于创建了两个person
的副本。
ECMAScript 5新增了Object.create()
方法,规范了原型式继承。这个方法接收两个参数:
Object.defineProperties()
方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任意属性都会覆盖原型对象上的同名属性
在只传入一个参数的时候和object()方法的行为相同。
包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
在只想让一个对象与另一个对象保持类似的情况下,可以使用原型式继承。
寄生式继承寄生式继承是与原型式继承紧密相关的一种思路,也是由克罗克福德提出的。
寄生式继承的思路与寄生构造函数和工厂模式类似,即:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像是它真的做了所有工作一样返回对象。
function object(o){
function F(){}
//将传进来的参数作为原型的值
F.prototype = o;
return new F();
}
//继承original
function createAnother(original){
//original是将要作为 新对象基础 的对象
//然后将这个对象传递给object(),返回值赋值给clone
var clone = object(original);//通过调用函数创建一个新对象
//给clone添加新方法sayHi()
clone.sayHi = function(){ //以某种凡是来增强这个对象
alert("Hi");
}
//返回clone对象
return clone;
}
//作为基础对象,可以理解为父类
var person = {
name:"Nicholas",
friends:["Shelby","Court","Van"]
};
//继承person,拥有person的属性和方法
var anotherPerson = createAnother(person);
//还有自己的方法
anotherPerson.sayHi();
console.log(anotherPerson.name);//Nicholas
console.log(anotherPerson.friends);// ["Shelby", "Court", "Van"]
object()
函数不是必须的,任何能够返回新对象的函数都适用于此模式。
使用寄生模式来为对象添加函数,会由于不能做到函数复用而降低效率,这个特点与构造函数模式类似。
寄生组合式继承(引用类型最理想的继承范式)寄生组合式继承:即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
其背后思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型的一个副本而已。
本质:就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
改造后的,就不会像组合继承中,会调用两次SuperType构造函数。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//该方法接收两个参数,一个是子类型构造函数,一个是超类型构造函数
function inheritPrototype(subType, superType){
//第一步创建超类型原型的一个副本
var prototype = object(superType.prototype); //创建对象
//因为上一步重新原型,导致prototype失去了默认的constructor属性,所以这一步手动指定
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构造函数
SuperType.call(this, name);
this.age = age;
}
//让SubType继承SuperType原型,inheritPrototype函数内部不会再次调用SuperType构造函数
inheritPrototype(SubType, SuperType);
//添加子类型自己的方法
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
YAHOO.lang.extend()方法采用了继承组合继承。。。。。。。。。。