手写实现 es5 继承 es6 继承

1,804 阅读3分钟

构造函数

在es6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和它们的特征。

创建对象可以通过以下三种方式:

1.对象字面量

var obj1 = {}

2.new Object()

var obj1 = new Object()

3.自定义构造函数

  function Student(name, age) {
    this.name = name
    this.age = age
    this.study = function() {
      console.log(this.name + '在学习')
    }
  }

构造函数和普通函数的不同

1、 书写方式不同,构造函数首字母习惯大写。

2、 作用不一样,构造函数用来新建实例对象。

3、 调用方式不一样,构造函数需要new 关键字来调用

4、 构造函数的函数名字和类名一样。

5、内部用this构建属性和方法, 构造函数的this指向的是对象实例本身

6、new 构造函数的执行流程

    1. 创建一个空对象
    1. 设置空对象的__proto__属性继承构造函数constructor的prototype属性,也就是继承构造函数的原型对象上的公有属性和方法
    1. 调用构造函数,将构造函数中的this替换为空对象的this,继承构造函数中的属性
    1. 在函数内部返回一个新对象 //简洁版的new操作符实现过程

    function new(constructor) { var obj = {}; //第一步:创建一个空对象obj obj.proto = constructor.prototype; //第二步:将构造函数 constructor的原型对象赋给obj的原型 contructor.apply(obj);//第三步:将构造函数 constructor中的this指向obj,并立即执行构造函数内部的操作 return obj; // 第四步:返回这个对象 }

缺点就是浪费内存 对象实例化时,构造函数都新的内存空间。 所以一般都会 用构造函数原型对象 :prototype 所有实例对象所共享原型方法。

7、用instanceof 可以检查一个对象是否是一个类的实例,是则返回true ;所有对象都是Object对象的后代,所以任何对象和Object做instanceof都会返回true

es5继承

两个类, 实现Child 继承 Parent

function Parent() {
 this.type = 'parent'
}
Parent.prototype.eat = function () {
 console.log('eat')
}

function Child(name) {
  this.name = name;
  this.color = 'black';
}

原型继承

// 将父类指向子类的原型。
Child.prototype = new Parent();
// 所有子类共享原型,改变一个其他也会改变

构造继承

// 在子类构造函数中调用父类构造函数

function Child(name) {
 Parent.call(this);
}
缺点:不能继承父类原型, 函数在构造函数中,每个子类实例不能共享函数

组合继承

使用构造函数继承父类参数, 使用原型继承父类函数
function Child(name) {
  Parent.call(this);
}

Child.prototype = Parent.prototype;

缺点:父亲原型和子类原型是同一个原型, 无法区分子类是谁构造的

寄生组合继承

在组合继承的基础上,子类继承一个由父类原型生成的对象。
function Child(name) {
  Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype, {
  constructor: {
    value: Child
  }
})

inherits 函数

  function inherits(child, parent) {
    child.super_ = parent;
    child.prototype = Object.create(parent.prototype, {
      constructor: {
        value: child,
        enumerable: false, // 否可枚举(enumerable)
        writable: true,  // 可写(writable)之类的
        configurable: true, //当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
      }
    })
  
  }

使用

function Child() {
  Parent.call(this);
}
inherits(Child, Parent);

Child.prototype.fun = ...

es6 继承

通过class和extends可以很直观的展示Child和Parent之间的继承关系,关键要点是constructor和super两个方法的使用

constructor是每个class中都必须拥有的,如果一开始没定义会自动添加一个空的constructor方法,当使用new的时候会自动调用这个函数

super是写在子类的constructor中的方法,这个super会调用父类的构造函数,这里的这个super也是必须写的也只能写在子类的constructor

class Parent {
  // contructor
   constructor(name) {
     this.name = name
   }
   // 其他私有方法
   say () {
     console.log(`我的名字叫${this.name}`);
   }
}
class Child extends Parent {
   constructor(name, age) {
     super(name);
     this.age = age;
   }
   getAge() {
     console.log(`我叫${this.name}今年${this.age}岁`);
   }
}

let p = new Child("child", 18);
console.log(p);
p.say();
p.getAge();