js继承es5、es6的7种方式

构造函数、原型对象

1.构造函数

function A(name) {
 this.name = name;
 this.arr = [1];
 this.say = function(){
   console.log('hi')
 }
}

注意:

  • 数组和方法都属于”实例引用属性“,但是数组强调私有、不共享的。方法需要复用、共享;
  • 在构造函数中,一般很少有数组形式的引用属性,大部分情况都是:基本属性+方法。

2.原型链

每个实例对象(Object)都有一个私有属性(称之为__proto__)指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(proto,层层向上知道一个对象的原型对象为null。根据定义,null没有原型,并作为这个原型链中的最后一个环节。

继承分类

js继承es5、es6的7种方式_第1张图片

继承方法 继承核心代码 优点 缺点
原型链继承 child.prototype = new parent() 父类方法可以复用 不可以传参, 实例的引用类型会被共享
构造函数继承 在子类child里执行parent.call(this} 实例的引用类型不共享 ,可以传参 父类的方法不能复用,子类实例的方法每次都是被单独创建的
组合式继承 child.prototype = new parent(); parent.call(this) 父类的方法可以被复用;父类的引用属性不会被共享; 子类构建实例时可以向父类传递参数 调用两次父类的构造函数,第一次给子类的原型添加了父类的name, arr属性;第二次又给子类的构造函数添加了父类的name, arr属性,从而覆盖了子类原型中的同名参数
原型式继承 原型式继承的object方法本质上是对参数对象的一个浅复制 父类方法可以复用 子类的引用属性会被所有子类的实例共享;子类构建实例时不能向父类传参;
寄生式继承 使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力 - -
寄生组合继承 - - -
es6 class extends - - -

1原型链继承

function parent(){
  this.name = 'mike';
}

parent.prototype.getName = function() {
  console.log(this.name)
}

function child(){
}

child.prototype = new parent();
child.prototype.constructor = child;

var instanceChild = new child();

instanceChild.getName() ;// 'mike;

原型链继承缺点:

  • 引用类型的属性被所有的实例共享;
function parent() {
 this.names = ['mike1', 'mike2'];
}

function child() {
}

child.prototype = new parent();
child.prototype.constructor = child;

var instanceChild = new child();
instanceChild.names.push = 'mike3';
console.log(instanceChild.names); // mike1, mike2, mike3

var instanceChild2 = new child();
instanceChild2.names.push = 'mike4';
console.log(instanceChild2.names); //mike1, mike2, mike3, mike4
  • 在创建child实例的时候,无法向parent传参;

2.借用构造函数(经典继承)

function parent(name) {
  this.names = name
}

function child(name) {
  parent.call(this, name)
}

var instanceChild = new child('mike');
console.log(instanceChild.name); // mike

var instanceChild2 = new child('mike2');
console.log(instanceChild2.name); //mike2

优点:

  • 避免了引用类型的属性被所有实例共享
  • 可以在child中向parent传参
    缺点:
  • 方法都在构造函数中定义,每次创建实例都会被创建一次方法。

3.组合式继承(原型链继承和借用构造函数合并)

function parent(name) {
  this.names = name
  this.body = ['head', 'foot']
}

function child(name, age) {
  parent.call(this, name)
  this.age = age
}

child.prototype = new parent()
child.prototype.constructor = child;

var instanceChild = new Child('mike', '8')
instanceChild.body.push('main')
console.log(instanceChild.name, instanceChild.age); // mike 8
console.log(instanceChild.body) // ['head', 'foot', 'main']

var instanceChild2 = new Child('mike2', '8')
instanceChild.body.push('main2')
console.log(instanceChild2.name, instanceChild.age); // mike2 8
console.log(instanceChild2.body) // ['head', 'foot', 'main2']

优点:

  • 解决了每个实例对引用类型属性的修改都会被其他实例共享的问题
  • 子类可以向父类传参
  • 可以实现父类方法复用
    缺点:
  • 需执行两次父类构造函数,第一次是parent.call(this, name),第二次是child.prototype = new parent()造成不必要的浪费;

4.原型式继承

复制传入的对象到创建对象的原型上,从而实现继承

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name : 'mike'
    body : ['head','foot']
}
var person1 = createObj(person)
var person2 = createObj(person)

console.log(person1) //mike
person1.body.push('main')
console.log(person2) //['head', 'foot', 'main']

缺点:

  • 同原型链继承一样,每个实例对引用类型属性的修改都会被其他实例共享;

ECMAScript5通过新增Object.create()方法规范化了原型式继承。这个方法接受了两个参数:一个用作新对象原型的对象和(可选)的一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同

var person1 = createObje(person); => var person1 = Object.create(person)

5.寄生式继承

可以使用Object.create来代替上述createObj的实现,原理基本上是一样的。寄生式继承其实就是createObj的内部某种形式来增强对象(这里的增强可以理解为添加对象的方法,最后返回增强之后的对象)

function createEnhanceObj(o) {
    //代替原型式继承的createObj
    var clone = Object.create(o) // 通过调用函数创建一个新对象
    clone.getName = function () {//以某种方式来增强这个对象
        console.log('hi')
    }
    return clone;
}

var person = {
 name: 'mike',
 body: ['head', 'foot']
}

var instancePerson = createEnhance(person)
instancePerson.getName(); // hi

缺点:

  • 同借用构造函数一样,无法复用父类函数,每次创建对象都会创建一遍方法;

6.寄生组合式继承

不需要为了子类的原型而多new了一次父类的构造函数,如Child.prototype = new Parent() 只需要复制父类原型的一个副本给子类原型即可

function inheritPrototype(Parent, Child){
	Child.prototype = Object.create(Parent.prototype) //创建父类原型的浅复制
	Child.prototype.constructor = Child; // 将子类的原型的构造函数替换
}

function Parent(name) {
    this.name = name
}

Parent.prototype.getNmae = function () {
    console.log(this.name)
}

function Child(color) {
    Parent.call(this, 'arzh')
    this.color = color
}

inheritPrototype(Parent, Child)

var arzhChild = new Child('red')
console.log(arzhChild.name) // 'arzh'

优点:
不必为了指定子类型的原型而调用父类型的构造函数

7.ES6继承

class Point {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    
    toString() {
        return this.x + '' + this.y
    }
}

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y) //调用父类的constructor(x, y)
        this.color = color
    }
    
    toString() {
        return this.color + ' ' + super.toString() // 调用父类的toString()
    }
}

var colorPoint = new ColorPoint('1', '2', 'red')

console.log(colorPoint.toString())  // red 12

参考文章:
[1]《js继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承》
[2]《前端必知必会ES5、ES6的7种继承》
[3]《JavaScript高级编程》

你可能感兴趣的:(前端,JavaScript)