javascript之继承(原型链)

1.原型链

每个构造函数都有一个原型对象,原型对象含有一个指向构造函数的指针,而实例包含一个指向原型对象的内部指针。将原型对象等于另一个类型的实例,那么此时的原型对象将包含一个指向另一个原型的指针,相应地另外一个原型中包含着一个指向另外一个构造函数的指针。假如另一个原型又是另外一个构造函数的实例,将依序前面所述关系,层层递进,就构成了实例与原型的链条。
实现原型代码模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}

function SubType(){
this.subProperty = false;
}

SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subProperty;
}

var instance = new subType();
alert(instance.getSuperValue()); // true;

原型代码模式

a.默认原型

所有函数的默认原型都是Object的实例,因此默认函数内部都包含一个指针,指向Object.prototype。这也正是所有自定义类型都会继承toString(),valueOf()等方法的原因。
原型代码模式

b.确定实例与原型之间的关系

两个方法:instanceof和isPrototypeof()

c.谨慎定义方法

i.子类型需要重写超类型中的方法,或者添加超类型中不存在的方法时,一定要把给原型添加的方法代码放到替换原型的代码之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//添加新方法
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//重写超类型中的方法
SubType.prototype.getSuperValue = function (){
return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

ii.在给子类型替换原型代码之后,不能以字面量对象的方式给子类型原型添加方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = {
getSubValue : function (){
return this.subproperty;
},
someOtherMethod : function (){
return false;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

d.原型链问题

第一个问题是包含引用类型值的原型,包含引用类型的值的原型属性会被所有实例所共享。
第二个问题是在创建子类型实例的时候,不能向超类型的构造函数中传递参数。

1
2
3
4
5
6
7
8
9
10
11
function SuperType(){
this.colors = ["red", "blue", "green"];
}

function SubType(){}
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
var instance2 = new SubType();
alert(instance2.colors); // "red, blue, green, black"

2.借用构造函数

基本思想:在子类型构造函数内部通过call或者apply()方法调用超类型构造函数。函数在特定环境下是对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
function SuperType(){
this.colors = ["red", "blue", "green"];
}

function SubType(){
SuperType.call(this);
}

var instance1 = new SubType();
var instance2 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); // "red, blue, green, black"
alert(instance2.colors); // "red, blue, green"

a.传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SuperType(name){
this.name = name;
}

function SubType(){
//继承了SuperType,同时还继承了属性
SuperType.call(this, "Nike");

//实例属性
this.age = 20;
}
var instance = new SubType();
alert(instance.name); // 'Nike'
alert(instance.age); // 20

在SubType构造函数中调用SuperType构造函数时,实际上是为SubType构造函数添加了name属性。

b.借用构造函数的问题

单独使用构造函数模式,会让方法无法复用。并且在超类型的原型中的方法,无法让子类型实例继承。

3.组合使用原型链和借用构造函数

思路:使用原型链实现对原型的属性和方法的继承,而通过借用构造来实现对实例属性的继承,从而发挥二者之长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function SuperType(name){
this.name = name;
this.colors = ["red", "yellow", "blue"];
}
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('Nike', 20);
instance1.colors.push("black");
alert(instance1.colors); //"red, yellow, blue, black"

var instance2 = new SubType('Tom', 18);
instance2.colors.push("pink");
alert(instance2.colors); // "red, yellow, blue, pink"

3. 原型式继承

道格拉斯·克罗克福德2006年提出的这个方法。这种方法并没有严格意义上创建构造函数。他的想法是借助原型可以基于已有的对象创建对象,同时还不必因此创建自定义类型。他给出的函数:

1
2
3
4
5
function object(o){
function F(){};
F.prototype = o;
return new F();
}

在 object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
var person = {
name: "Nicholas",
friends: ["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"

ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。
第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。
支持 Object.create()方法的浏览器有 IE9+、 Firefox 4+、 Safari 5+、 Opera 12+和 Chrome。

1
2
3
4
5
6
7
8
9
10
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"