前言
由于函数没有签名,在ECMAScript中无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。因此我们先来了解一下什么是原型链。
提示:以下是本篇文章正文内容,下面案例可供参考
一、原型链是什么?
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。
二、继承
1.原型链继承
代码如下(示例):
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
}
function SubType () {
this.subProperty = false;
}
SubType.prototype.getSubValue = function () {
return this.subProperty;
}
// 继承了SuperType
SubType.prototype = new SuperType()
var instancel = new SubType();
console.log(instancel.getSuperValue()); //true
- 注意
- 1.包含引用类型值的原型属性会被所有实例共享
- 2.创建子类型的实例时,不能向超类型的构造函数中传递参数
2.构造函数式继承
代码如下(示例):
function SuperType (name) {
this.name = name;
this.color = ['red', 'blue', 'green'];
}
function SubType () {
// 通过使用apply()和call()方法也可以在新创建的对象上执行构造函数
// 继承了SuperType
SuperType.call(this,'小明')
}
var instancel1 = new SubType();
instancel1.color.push('yellow'); //(4) ['red', 'blue', 'green', 'yellow']
console.log(instancel1.color);
var instancel2 = new SubType();
console.log(instancel2.color);//(3) ['red', 'blue', 'green']
- 优点
- 1.在继承父类的同时还可传递参数
- 2.解决了引用类型值被共享的问题
- 缺点
- 1.方法都在构造函数中定义,函数复用性不高
- 2.父类定义的方法对子类不可见
3.组合继承
- 注
- 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性
代码如下(示例):
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.sayAge = function () {
console.log(this.age);
}
var instancel = new SubType('小明', 20)
instancel.colors.push('yellow');
console.log(instancel1.colors);//(4) ['red', 'blue', 'green', 'yellow']
instancel1.sayName();//小明
instancel1.sayAge();//20
var instance2 = new SubType('小芳', 10)
instancel.colors.push('black');
console.log(instance2.colors);//(4) ['red', 'blue', 'green', 'black']
instance2.sayName();//小芳
instance2.sayAge();//10
4.原型式继承
- 注
- 必须有一个对象可以作为另一个对象的基础
代码如下(示例):
function object (o) {
function Fun () { }; //创建临时的构造函数
Fun.prototype = o; //将传入对象作为该构造函数的原型 浅复制
return new Fun();
}
// 基础对象
// 将person作为原型,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性
var person = {
name: '喜羊羊', //基本类型值属性
friends: ['懒羊羊', '肥羊羊'] //引用类型值属性
}
// 这意味着person.friends不仅属于person所有,而且也会被anotherPerson以及yetAnotherPerson共享。
var anotherPerson = object(person);
anotherPerson.name = '暖羊羊';
anotherPerson.friends.push('美羊羊');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "灰太狼";
yetAnotherPerson.friends.push('小灰灰');
console.log(person.name, person.friends); //喜羊羊 (4) ['懒羊羊', '肥羊羊', '美羊羊', '小灰灰']
console.log(anotherPerson.name, anotherPerson.friends); //暖羊羊 (4) ['懒羊羊', '肥羊羊', '美羊羊', '小灰灰']
console.log(yetAnotherPerson.name, yetAnotherPerson.friends); //灰太狼 (4) ['懒羊羊', '肥羊羊', '美羊羊', '小灰灰']
-
优点
-
1.一个对象与另一个对象保持类似
-
缺点
-
1.引用类型值的属性始终都会共享相应的值
-
2.子类不能向父类传参
ECMAScript 5通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同。
5.寄生式继承
- 思路
- 创建一个仅用于封装继承过程的函数 该函数内部以某种方式增强对象
代码如下(示例):
function createAnther (original) {
var clone = object(original); //创建临时的构造函数
clone.sayHi = function () {
console.log('hi' + ' ' + clone.name);
}
return clone;
}
// createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给object()函数,将返回的结果赋值给clone。再为clone对象添加一个新方法sayHi(),最后返回clone对象。
var person = {
name: '喜羊羊',
friends: ['懒羊羊', '肥羊羊']
}
var anotherPerson = createAnther(person);
anotherPerson.sayHi(); //hi 喜羊羊
- 适用于 主考虑对象而不是自定义类型和构造函数的情况 任何能够返回新对象的函数都适用于此模式。
- 缺点
- 不能做到函数复用
6.寄生组合式继承
代码如下(示例):
function inheritPrototype (subType, superType) {
let prototype = Object(superType.prototype);
prototype.construetor = subType;
subType.prototype = prototype;
}
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;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
return this.age;
}
var instancel1 = new SubType('小明', 20);
console.log(instancel1.sayAge());
总结
这里对文章进行总结:
javascript主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。
□ 原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以得到进一步改造。
□ 寄生式继承,与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致的低效率问题,可以将这个模式与组合继承一起使用。
□ 寄生组合式继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。