原型链继承、
ECMAScript主要依靠原型链实现继承
利用原型让一个引用类型继承另一个引用类型的实例和方法。让子类原型对象等于超类对象的实例。
function SuperType() {
this.property = "super";
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subProperty = "sub";
}
//让SubType原型对象等于SuperType的实例,
//此时SubType的原型对象的constructor指向SuperType,因为SubType的原型指向了SuperType的原型;
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.property;
}
var subInstance = new SubType();
console.log(subInstance.getSuperValue());//super
默认原型
所有的引用类型默认通过原型链继承Object,所有函数的默认原型都是Object实例。
确定原型和实例的关系
instanceof
操作符,检测实例所在的原型链是否出现过某个构造函数。用来检测constructor.prototype 是否存在于参数 object 的原型链上。
语法
object instanceof constructor 参数 object 要检测的对象. constructor 某个构造函数
console.log(subInstance instanceof SubType);//true
console.log(subInstance instanceof SuperType);//true
console.log(subInstance instanceof Object);//true
isPropertyOf()
方法用于测试一个对象是否存在于另一个对象的原型链上。
console.log(SubType.prototype.isPrototypeOf(subInstance));//true
console.log(SuperType.prototype.isPrototypeOf(subInstance));//true
console.log(Object.prototype.isPrototypeOf(subInstance));//true
给原型添加的方法一定要放在替换原型的语句之后。
原型链的问题
问题1,原型对象中包含引用类型被共享,对引用类型属性的操作会影响所有实例该属性。
问题2,创建子类型的实例时,不能向超类型的构造函数传递参数。
function SuperType() {
this.colors = ["yellow", "blue", "red"];
}
function SubType() { }
//此时SubType.prtotype变成了SuperType的一个实例,因此拥有一个colors属性,定义在原型对象中的这个colors属性被所有SubType实例共享
SubType.prototype = new SuperType();
var sub1 = new SubType();
sub1.colors.push("newColor");
var sub2 = new SubType();
console.log(sub1.colors);// ["yellow", "blue", "red", "newColor"]
//被影响
console.log(sub2.colors);// ["yellow", "blue", "red", "newColor"]
借用构造函数
可以解决原型链问题
在子类型构造函数中调用超类构造函数,其实函数只不过是在特定环境中执行代码的对象
,通过apply()或call()方法执行构造函数。
function SuperType(name) {
this.colors = ["yellow", "blue", "red"];
}
//超类原型上定义属性
SuperType.prototype.sayName = "hh";
function SubType() {
//执行超类构造函数,每个SubType的实例会拥有各自的colors属性的副本
SuperType.call(this);
}
var sub1 = new SubType();
sub1.colors.push("newColor");
var sub2 = new SubType();
console.log(sub1.colors);// ["yellow", "blue", "red", "newColor"]
console.log(sub2.colors);// ["yellow", "blue", "red"]
//超类原型定义的属性子类不可见
console.log(sub1.sayName);//undefined
借用构造函数的问题:方法都在构造函数中定义,因此函数复用就无从谈起了。在超类原型上定义的属性子类不可见
组合继承
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,即通过在原型上定义方法实现了函数的复用,又保证每个实例都有自己的属性。
function SuperType(name) {
this.name = name;
this.colors = ["yellow", "blue", "red"];
}
SuperType.prototype.sayName = function () {
console.log(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 () {
console.log(this.age);
}
var sub1 = new SubType("xixi", 29);
sub1.colors.push("newColor");
console.log(sub1.colors);// ["yellow", "blue", "red", "newColor"]
sub1.sayName();//xixi
sub1.sayAge();//29
var sub2 = new SubType("haha", 18);
console.log(sub2.colors);// ["yellow", "blue", "red"]
sub2.sayName();//haha
sub2.sayAge();//18
组合继承会调用两次超类构造函数。一次在创建子类原型时,另一次是在子类型构造函数内部。
原型式继承
基于已有的对象创建新对象
//道格拉斯.克罗福德给出如下函数
function object(o) {
//创建一个临时构造函数
function F() {}
//将传入的对象作为这个构造函数的原型
F.prototype = o;
return new F();
}
eg.
var person = {
name: "Jack",
friends: ["f1", "f2"]
};
var anotherPerson = object(person);
anotherPerson.name = "Tom";
anotherPerson.friends.push("f3");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Jerry";
yetAnotherPerson.friends.push("f4");
console.log(person.friends);//["f1", "f2", "f3", "f4"]
Object.create(proto, [propertiesObject])
proto
新创建对象的原型对象。
propertiesObject
可选。如果没有指定为 undefined。
这些属性对应Object.defineProperties()的第二个参数: 每个属性都是通过自己的描述符定义的。
这个方式指定的属性会覆盖原型对象上的同名属性。
在传入一个参数的情况下,与object的行为相同。
var person = {
name: "Jack",
friends: ["f1", "f2"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Tom";
anotherPerson.friends.push("f3");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Jerry";
yetAnotherPerson.friends.push("f4");
console.log(person.friends);//["f1", "f2", "f3", "f4"]
传入两个参数
var person = {
name: "Jack",
friends: ["f1", "f2"]
};
var anotherPerson = Object.create(person, {
name: {
value: "Tom"
}
});
console.log(anotherPerson.name);//Tom
寄生式继承
创建一个仅用于封装继承过程的函数。内部创建一个对象,并增强该对象,最后返回该对象。
//接收一个对象
function createAnother(original) {
var clone = object(original);//创建一个新对象,任何能够返回新对象的函数都适用于此模式
clone.sayHi = function() { //增强
console.log("hi");
}
return clone;
}
eg.
var person = {
name: "Jack",
friends: ["f1", "f2"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();//hi
寄生组合式继承
组合继承调用两次超类构造函数。
function SuperType(name) {
this.name = name;
this.colors = ["yellow", "blue", "red"];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function SubType(name, age) {
//通过构造函数继承
SuperType.call(this, name);
this.age = age;
}
//通过原型链继承
SubType.prototype = new SuperType();//第一次调用超类构造函数,SubType.prototype内含有name和colors属性。
SubType.prototype.constructor = SubType;
var sub = new SubType("Jack", 29);//第二次调用超类构造函数,sub实例对象又会创建name和colors属性。
通过寄生组合式继承,可以只调用一次超类构造函数。思路:不必为了指定子类型的原型调用超类构造函数。
function inheritPrototype(subType, superType) {
//创建超类对象原型的副本
var prototype = object(superType.prototype);
prototype.constructor = SubType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.color = ["yellow", "blue", "red"];
}
superType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);//只调用一次超类构造函数
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
}