JS
系列暂定 27 篇,从基础,到原型,到异步,到设计模式,到架构模式等,前端
本篇是 JS
系列中第 3 篇,文章主讲 JS
继承,包括原型链继承、构造函数继承、组合继承、寄生组合继承、原型式继承、 ES6 继承,以及 多继承与 new 。git
先定义一个父类github
function SuperType () {
// 属性
this.name = 'SuperType';
}
// 原型方法
SuperType.prototype.sayName = function() {
return this.name;
};
复制代码
将父类的实例做为子类的原型设计模式
// 父类
function SuperType () {
this.name = 'SuperType'; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
// 子类
function SubType () {
this.subName = "SubType"; // 子类属性
};
SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例
// 这里实例化一个 SuperType 时, 实际上执行了两步
// 1,新建立的对象复制了父类构造函数内的全部属性及方法
// 2,并将原型 __proto__ 指向了父类的原型对象
SubType.prototype.saySubName = function () { // 子类原型方法
return this.subName;
}
// 子类实例
let instance = new SubType();
// instanceof 经过判断对象的 prototype 链来肯定对象是不是某个类的实例
instance instanceof SubType; // true
instance instanceof SuperType; // true
// 注意
SubType instanceof SuperType; // false
SubType.prototype instanceof SuperType ; // true
复制代码
利用原型,让一个引用类型继承另外一个引用类型的属性及方法架构
继承了父类的模板,又继承了父类的原型对象app
能够在子类构造函数中,为子类实例增长实例属性。若是要新增原型属性和方法,则必须放在 SubType.prototype = new SuperType('SubType');
这样的语句以后执行。异步
没法实现多继承ide
来自原型对象的全部属性被全部实例共享函数
// 父类
function SuperType () {
this.colors = ["red", "blue", "green"];
this.name = "SuperType";
}
// 子类
function SubType () {}
// 原型链继承
SubType.prototype = new SuperType();
// 实例1
var instance1 = new SubType();
instance1.colors.push("blcak");
instance1.name = "change-super-type-name";
console.log(instance1.colors); // ["red", "blue", "green", "blcak"]
console.log(instance1.name); // change-super-type-name
// 实例2
var instance2 = new SubType();
console.log(instance2.colors); // ["red", "blue", "green", "blcak"]
console.log(instance2.name); // SuperType
复制代码
注意:更改 SuperType
引用类型属性时,会使 SubType
全部实例共享这一更新。基础类型属性更新则不会。post
建立子类实例时,没法向父类构造函数传参,或者说是,没办法在不影响全部对象实例的状况下,向超类的构造函数传递参数
在子类型的构造函数内部调用父类型构造函数。
函数只不过是在特定环境中执行代码的对象,因此这里使用 apply/call 来实现。
使用父类的构造函数来加强子类实例,等因而复制父类的实例属性给子类(没用到原型)
// 父类
function SuperType (name) {
this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
// 子类
function SubType () {
// 调用 SuperType 构造函数
SuperType.call(this, 'SuperType'); // 在子类构造函数中,向父类构造函数传参
// 为了保证子父类的构造函数不会重写子类的属性,须要在调用父类构造函数后,定义子类的属性
this.subName = "SubType"; // 子类属性
};
// 子类实例
let instance = new SubType(); // 运行子类构造函数,并在子类构造函数中运行父类构造函数,this绑定到子类
复制代码
解决了1中子类实例共享父类引用对象的问题,实现多继承,建立子类实例时,能够向父类传递参数
顾名思义,组合继承就是将原型链继承与构造函数继承组合在一块儿,从而发挥二者之长的一种继承模式。
使用原型链继承使用对原型属性和方法的继承,经过构造函数继承来实现对实例属性的继承。这样既能经过在原型上定义方法实现函数复用,又能保证每一个实例都有本身的属性。
经过调用父类构造,继承父类的属性并保留传参的优势,而后经过将父类实例做为子类原型,实现函数复用
// 父类
function SuperType (name) {
this.colors = ["red", "blue", "green"];
this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
// 子类
function SubType (name, subName) {
// 调用 SuperType 构造函数
SuperType.call(this, name); // ----第二次调用 SuperType----
this.subName = subName;
};
// ----第一次调用 SuperType----
SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例
SubType.prototype.constructor = SubType; // 组合继承须要修复构造函数指向
SubType.prototype.saySubName = function () { // 子类原型方法
return this.subName;
}
// 子类实例
let instance = new SubType('An', 'sisterAn')
instance.colors.push('black')
console.log(instance.colors) // ["red", "blue", "green", "black"]
instance.sayName() // An
instance.saySubName() // sisterAn
let instance1 = new SubType('An1', 'sisterAn1')
console.log(instance1.colors) // ["red", "blue", "green"]
instance1.sayName() // An1
instance1.saySubName() // sisterAn1
复制代码
第一次调用 SuperType
构造函数时,SubType.prototype
会获得两个属性name
和colors
;当调用 SubType
构造函数时,第二次调用 SuperType
构造函数,这一次又在新对象属性上建立了 name
和colors
,这两个属性就会屏蔽原型对象上的同名属性。
// instanceof:instance 的原型链是针对 SuperType.prototype 进行检查的
instance instanceof SuperType // true
instance instanceof SubType // true
// isPrototypeOf:instance 的原型链是针对 SuperType 自己进行检查的
SuperType.prototype.isPrototypeOf(instance) // true
SubType.prototype.isPrototypeOf(instance) // true
复制代码
弥补了方式2的缺陷,能够继承实例属性/方法,也能够继承原型属性/方法,不存在引用属性共享问题,可传参,可复用
在组合继承中,调用了两次父类构造函数,这里 经过经过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
借用 构造函数 继承 属性 ,经过 原型链的混成形式 来继承 方法
// 父类
function SuperType (name) {
this.colors = ["red", "blue", "green"];
this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
// 子类
function SubType (name, subName) {
// 调用 SuperType 构造函数
SuperType.call(this, name); // ----第二次调用 SuperType,继承实例属性----
this.subName = subName;
};
// ----第一次调用 SuperType,继承原型属性----
SubType.prototype = Object.create(SuperType.prototype)
SubType.prototype.constructor = SubType; // 注意:加强对象
let instance = new SubType('An', 'sisterAn')
复制代码
SuperType
构造函数,只建立一份父类属性instanceof
与 isPrototypeOf
实现思路就是将子类的原型设置为父类的原型
// 父类
function SuperType (name) {
this.colors = ["red", "blue", "green"];
this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
/** 第一步 */
// 子类,经过 call 继承父类的实例属性和方法,不能继承原型属性/方法
function SubType (name, subName) {
SuperType.call(this, name); // 调用 SuperType 的构造函数,并向其传参
this.subName = subName;
}
/** 第二步 */
// 解决 call 没法继承父类原型属性/方法的问题
// Object.create 方法接受传入一个做为新建立对象的原型的对象,建立一个拥有指定原型和若干个指定属性的对象
// 经过这种方法指定的任何属性都会覆盖原型对象上的同名属性
SubType.prototype = Object.create(SuperType.prototype, {
constructor: { // 注意指定 SubType.prototype.constructor = SubType
value: SubType,
enumerable: false,
writable: true,
configurable: true
},
run : {
value: function(){ // override
SuperType.prototype.run.apply(this, arguments);
// call super
// ...
},
enumerable: true,
configurable: true,
writable: true
}
})
/** 第三步 */
// 最后:解决 SubType.prototype.constructor === SuperType 的问题
// 这里,在上一步已经指定,这里不须要再操做
// SubType.prototype.constructor = SubType;
var instance = new SubType('An', 'sistenAn')
复制代码
若是但愿能 多继承 ,可以使用 混入 的方式
// 父类 SuperType
function SuperType () {}
// 父类 OtherSuperType
function OtherSuperType () {}
// 多继承子类
function AnotherType () {
SuperType.call(this) // 继承 SuperType 的实例属性和方法
OtherSuperType.call(this) // 继承 OtherSuperType 的实例属性和方法
}
// 继承一个类
AnotherType.prototype = Object.create(SuperType.prototype);
// 使用 Object.assign 混合其它
Object.assign(AnotherType.prototype, OtherSuperType.prototype);
// Object.assign 会把 OtherSuperType 原型上的函数拷贝到 AnotherType 原型上,使 AnotherType 的全部实例均可用 OtherSuperType 的方法
// 从新指定 constructor
AnotherType.prototype.constructor = AnotherType;
AnotherType.prototype.myMethod = function() {
// do a thing
};
let instance = new AnotherType()
复制代码
SuperType.call
继承实例属性方法Object.create()
来继承原型属性与方法SubType.prototype.constructor
的指向首先,实现一个简单的 ES6 继承:
class People {
constructor(name) {
this.name = name
}
run() { }
}
// extends 至关于方法的继承
// 替换了上面的3行代码
class Man extends People {
constructor(name) {
// super 至关于属性的继承
// 替换了 People.call(this, name)
super(name)
this.gender = '男'
}
fight() { }
}
复制代码
extends
继承的核心代码以下,其实现和上述的寄生组合式继承方式同样
function _inherits(subType, superType) {
// 建立对象,Object.create 建立父类原型的一个副本
// 加强对象,弥补因重写原型而失去的默认的 constructor 属性
// 指定对象,将新建立的对象赋值给子类的原型 subType.prototype
subType.prototype = Object.create(superType && superType.prototype, {
constructor: { // 重写 constructor
value: subType,
enumerable: false,
writable: true,
configurable: true
}
});
if (superType) {
Object.setPrototypeOf
? Object.setPrototypeOf(subType, superType)
: subType.__proto__ = superType;
}
}
复制代码
注: 考虑到JavaScript的工做方式,因为原型链等特性的存在,在不一样对象之间功能的共享一般被叫作 委托 - 特殊的对象将功能委托给通用的对象类型完成。这也许比将其称之为继承更为贴切,由于“被继承”了的功能并无被拷贝到正在“进行继承”的对象中,相反它仍存在于通用的对象中。
想看更过系列文章,点击前往 github 博客主页
1. ❤️玩得开心,不断学习,并始终保持编码。👨💻
2. 若有任何问题或更独特的看法,欢迎评论或直接联系瓶子君(公众号回复 123 便可)!👀👇
3. 👇欢迎关注:前端瓶子君,每日更新!👇