目录
1.原型链继承
2.借用构造函数继承
3.组合继承(经典继承)
4.原型式继承
方法一:
方法二:()
5.寄生式继承
6.寄生组合式继承
7.ES6、Class实现继承
JavaScript想实现继承的目的:重复利用另外一个对象的属性和方法。
1.原型链继承
让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性。
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
function Parent() {
= true
= {
name: "mjy",
age: 18,
};
}
= function() {
();
();
}
function Child() {};
= new Parent();
let Child1 = new Child();
= "男";
(); // {name: 'mjy', age: 18, gender: '男'} ture
let child2 = new Child();
= false
() // 男
(); // {name: 'mjy', age: 18, gender: '男'} false
优点:写法方便简洁,容易理解。
缺点:对象实例共享所有继承的属性和方法。传教子类型实例的时候,不能传递参数,因为这个对象是一次性创建的(没办法定制化)。
2.借用构造函数继承
function Parent(gender) {
= {
name: "yhd",
age: 19,
gender: gender
}
}
function Child(gender) {
(this, gender)
}
let child1 = new Child('男');
= 'xiaoma'
();
let child2 = new Child('女');
();
在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
3.组合继承(经典继承)
将
原型链
和借用构造函数
的组合到一块。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性
function Person(gender) {
('执行次数');
= {
name: "mjy",
age: 19,
gender: gender
}
}
= function () { // 使用原型链继承原型上的属性和方法
(, )
}
function Child(gender) {
(this, gender) // 使用构造函数法传递参数
}
= new Person()
let child1 = new Child('男');
= 'xiaoma'
()
();
let child2 = new Child('女');
();
优点: 解决了原型链继承和借用构造函数继承造成的影响。
缺点: 无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
4.原型式继承
方法一:
借用构造函数在一个函数A内部创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
本质上,函数A是对传入的对象执行了一次浅复制。
function createObject(obj) {
function Fun() {}
= obj
return new Fun()
}
let person = {
name: 'mjy',
age: 18,
hoby: ['唱', '跳'],
showName() {
('my name is:', )
}
}
let child1 = createObject(person)
= 'xxxy'
('rap')
let child2 = createObject(person)
(child1)
(child2)
() // ['唱', '跳', 'rap']
方法二:()
() 是把现有对象的属性,挂到新建对象的原型上,新建对象为空对象
ECMAScript 5通过增加()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,()与这里的函数A方法效果相同。
let person = {
name: 'mjy',
age: 19,
hoby: ['唱', '跳'],
showName() {
('my name is: ', )
}
}
let child1 = (person)
= 'xxt'
('rap')
let child2 = (person)
(child1)
(child2)
() // ['唱', '跳', 'rap']
优点是:不需要单独创建构造函数。
缺点是:属性中包含的引用值始终会在相关对象间共享,子类实例不能向父类传参
5.寄生式继承
寄生式继承的思路与(寄生) `原型式继承` 和 `工厂模式` 似, 即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。
function objectCopy(obj) {
function Fun() { };
= obj;
return new Fun();
}
function createAnother(obj) {
let clone = objectCopy(obj);
= function () {
('my name is:', );
};
return clone;
}
let person = {
name: "mjy",
age: 18,
hoby: ['唱', '跳']
}
let child1 = createAnother(person);
("rap");
(); // ['唱', '跳', 'rap']
(); // my name is: mjy
let child2 = createAnother(person);
(); // ['唱', '跳', 'rap']
优点:写法简单,不需要单独创建构造函数。
缺点:通过寄生式继承给对象添加函数会导致函数难以重用。使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.
6.寄生组合式继承
前面讲过,组合继承是常用的经典继承模式,不过,组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数;一次是在创建子类型的时候,一次是在子类型的构造函数内部。寄生组合继承就是为了降低父类构造函数的开销而实现的。
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function objectCopy(obj) {
function Fun() { };
= obj;
return new Fun();
}
function inheritPrototype(child, parent) {
let prototype = objectCopy();
= child;
= prototype;
}
function Parent(name) {
= name;
= ['唱', '跳']
}
= function () {
('my name is:', );
}
function Child(name, age) {
(this, name);
= age;
}
inheritPrototype(Child, Parent);
= function () {
('my age is:', );
}
let child1 = new Child("mjy", 18);
(); // 18
(); // mjy
("rap");
(); // ['唱', '跳', 'rap']
let child2 = new Child("yl", 18);
(); // 18
(); // yl
(); // ['唱', '跳']
优点是:高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变;
缺点是:代码复杂
7.ES6、Class实现继承
原理ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面((this))。 ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this
优点:语法简单易懂,操作更方便。
缺点:并不是所有的浏览器都支持class关键字 lass Per