ECMASCript只支持实现继承,主要是依靠原型链来实现的。
实现一个简单的原型链
//SuperType类型 function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function (){ return this.property } //SubType类型 function SubType(){ this.subproperty = false; } // 重写了原型对象,SubType继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; } let instance = new SubType(); // 注意instance能使用SuperValue的方法 console.log(instance.getSuperValue())//true
所有引用类型默认继承了Object,而所有函数的默认原型都是Object的实例。
console.log(instance instanceof Object);//true console.log(instance instanceof SuperType);//true console.log(instance instanceof SubType);//true
也可以使用isPrototypeOf()方法
console.log(Object.prototype.isPrototypeOf(instance)); console.log(SuperType.prototype.isPrototypeOf(instance)); console.log(SubType.prototype.isPrototypeOf(instance));
注意通过原型链实现继承的时候,不要使用对象字面量创建原型方法。因为这样就会重写原型链,导致继承关系失效。
原型链第一个问题就是原型链会导致继承的prototype的属性会“共享”,实例之间会相互影响,第二问题就是无法在不影响其他实例情况下给超类的构造函数传参。实际上很少单独使用原型链。
1.组合继承
function SuperType(name){ this.name=name; this.color=['red','blue','green']; } 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) }; let instance1 = new SubType("nick", 29); instance1.color.push("black"); console.log(instance1.color); instance1.sayName(); instance1.sayAge(); let instance2 = new SubType("grep", 27); console.log(instance2.color); instance2.sayName(); instance2.sayAge();
2.原型式继承
// 对参数o进行一次浅复制 function object(o){ function F(){} F.prototype = o; return new F() } // 作为另一个对象的基础 let person = { name:"nick", friends:['shellby','court','van'] }; let person2 = object(person); person2.name = 'grep'; person2.friends.push("Rob"); let person3 = object(person); person3.name = 'lina'; person3.friends.push("Barbie"); console.log(person.friends)//[ 'shellby', 'court', 'van', 'Rob', 'Barbie' ]
person2和person3有着与person一样的共享属性
ES5中使用Object.create来替代object方法,并且可以传入第二个参数,一个为新对象定义额外属性的对象。
3.寄生式继承
function createAnother(ori){ // 创建一个新对象 let clone = Object.create(ori); // 增强这个对象 clone.sayHi = function (){ console.log("hi") }; return clone } let person = { name:"nick", friends:['shell','court','van'] }; let person1 = createAnother(person); person1.sayHi();
4 寄生组合式继承
组合继承的缺点就是会两次调用父类的构造函数,可以直接使用父类原型的复制副本。
// 子类构造函数和父类构造函数 function inheritPrototype(subType,superType){ let prototype = Object.create(superType.prototype); prototype.constructor = subType; subType.prototype = prototype } function SuperType(name){ this.name = name; this.colors = ['red', 'blue', 'green']; } SuperType.prototype.sayName = function (){ console.log(this.name) } function SubType(name,age){ SuperType.call(this,name) this.age = age } // 实际上就是复制父类的prototype方法 inheritPrototype(SubType,SuperType); SubType.prototype.sayAge = function (){ console.log(this.age) } let instance = new SubType('nick', '29') console.log(instance.colors,instance.name,instance.age) instance.sayAge() let instance2 = new SubType('grep', '27') console.log(instance2.colors,instance2.name,instance2.age) instance2.sayAge()
这样只需要每次调用一次构造函数就行了。