Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js 继承的实现 #19

Open
wengjq opened this issue Feb 22, 2018 · 0 comments
Open

js 继承的实现 #19

wengjq opened this issue Feb 22, 2018 · 0 comments

Comments

@wengjq
Copy link
Owner

wengjq commented Feb 22, 2018

父类

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function () {
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function (food) {
  console.log(this.name + '正在吃:' + food);
};

原型链继承

核心:将父类的实例作为子类的原型。

function Cat () { 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); // true 
console.log(cat instanceof Cat); // true

特点:

  • 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点:

  • 要想为子类新增属性和方法,必须要在替换原型 new Animal() 这样的语句之后执行,不能放到构造器中
  • 来自原型对象的引用类型的属性是所有实例共享的
  • 创建子类实例时,无法向父类构造函数传参

借用构造函数继承

核心:子类构造函数调用父类的构造函数,等于是复制父类的实例属性给子类(没用到原型)。

function Cat (name) {
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:

  • 解决了原型链继承中,子类实例共享父类引用属性的问题
  • 创建子类实例时,可以向父类传递参数

缺点:

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

function Cat (name) {
  Animal.call(this);
  this.name = name || 'Tom';
}

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点:

  • 可以继承实例属性/方法,也可以继承原型属性/方法
  • 不存在引用类型属性共享问题
  • 可传参
  • 函数可复用

缺点:

  • 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

原型式继承

核心:借助原型基于已有的对象创建新对象。

function object (o) {
  function F () {}
  F.prototype = o;
  return new F();
}
var Cat = object(Animal);
console.log(Cat.name);

ES5 规范了原型式继承,如下:

var Cat = Object.create(Animal);
console.log(Cat.name);

特点:

  • 可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以得到进一步改造。

缺点:

  • 存在引用类型属性共享问题

寄生式继承

核心:创建一个仅用来封装继承过程的函数,该函数在内部以某种方式来增强对象。

function object (o) {
  function F () {}
  F.prototype = o;
  return new F();
}

function createAnother (o) {
  var clone = object(o);
  clone.sayHi = function () {
  	console.log('hi');
  }
  return clone;
}

var Cat = createAnother(Animal);
Cat.sayHi();

特点:

  • 可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以得到进一步改造。

缺点:

  • 存在引用类型属性共享问题
  • 函数不能复用

寄生组合继承

核心:通过寄生方式,去掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点。

function Cat (name) {
  Animal.call(this);
  this.name = name || 'Tom';
}
(function () {
  // 创建一个没有实例方法的类
  var Super = function () {};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();

  // ES5 直接用下面一句搞定
  // Cat.prototype = Object.create(Animal.prototype);
})();

// Test Code
var cat = new Cat();
Cat.prototype.constructor = Cat; 

console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点:

  • 堪称完美

缺点:

  • 实现较为复杂
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant