JavaScript继承

一、ES5通过修改原型链实现继承

https://wangdoc.com/javascript/oop/prototype.html

1.构造函数的继承——子类整体继承父类

让一个构造函数继承另一个构造函数。

  • 第一步,子类继承父类的实例
  • 第二步,子类继承父类的原型

第一步:在子类的构造函数中,调用父类的构造函数

1 function Sub(value) {
2   Super.call(this);
3   this.prop = value;
4 }

上面代码中,Sub是子类的构造函数,this是子类的实例。在实例上调用父类的构造函数Super,就会让子类实例具有父类实例的属性。

第二步:让子类的原型指向父类的原型(这样子类就可以继承父类的原型)

Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';

上面代码中,Sub.prototype是子类的原型,要将它赋值为Object.create(Super.prototype),而不是直接等于Super.prototype。否则后面两行对Sub.prototype的操作,会连父类的原型Super.prototype一起修改掉。

举例来说,下面是一个Shape构造函数。

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

我们需要让Rectangle构造函数继承Shape

// 第一步,子类继承父类的实例
function Rectangle() {
  Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
  this.base = Shape;
  this.base();
}

// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

采用这样的写法以后,instanceof运算符会对子类和父类的构造函数,都返回true

var rect = new Rectangle();

rect instanceof Rectangle  // true
rect instanceof Shape  // true

2.单个方法的继承

上面代码中,子类是整体继承父类。有时只需要单个方法的继承,这时可以采用下面的写法。

ClassB.prototype.print = function() {
  ClassA.prototype.print.call(this);
  // some code
}

上面代码中,子类Bprint方法先调用父类Aprint方法,再部署自己的代码。这就等于继承了父类Aprint方法。 

3.多重继承

https://wangdoc.com/javascript/oop/prototype.html#%E5%8E%9F%E5%9E%8B%E5%AF%B9%E8%B1%A1%E6%A6%82%E8%BF%B0

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create

JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

 1 function M1() {
 2   this.hello = 'hello';
 3 }
 4 
 5 function M2() {
 6   this.world = 'world';
 7 }
 8 
 9 function S() {
10   M1.call(this);
11   M2.call(this);
12 }
13 
14 // 继承 M1
15 S.prototype = Object.create(M1.prototype);
16 // 继承链上加入 M2
17 Object.assign(S.prototype, M2.prototype);
18 
19 // 指定构造函数
20 S.prototype.constructor = S;
21 
22 var s = new S();
23 s.hello // 'hello'
24 s.world // 'world'

上面代码中,子类S同时继承了父类M1M2。这种模式又称为 Mixin(混入)。

Object.assign 会把 M2 原型上的函数拷贝到 S 原型上,使 S 的所有实例都可用 M2的方法。

Object.create() 创建实例对象实现继承父类的属性和方法,但还没有继承父类原型

https://wangdoc.com/javascript/oop/new.html

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create

构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

 1 var person1 = {
 2   name: '张三',
 3   age: 38,
 4   greeting: function() {
 5     console.log('Hi! I\'m ' + this.name + '.');
 6   }
 7 };
 8 
 9 var person2 = Object.create(person1);
10 
11 person2.name // 张三
12 person2.greeting() // Hi! I'm 张三.

上面代码中,对象person1person2的模板,后者继承了前者的属性和方法。 

super关键字

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/super

super关键字用于访问和调用一个对象的父对象上的函数。

语法

super([arguments]); 
// 调用 父对象/父类 的构造函数

super.functionOnParent([arguments]); 
// 调用 父对象/父类 上的方法

描述:在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。super关键字也可以用来调用父对象上的函数。

call() 方法

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

返回值:使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

使用 call 方法调用父构造函数

在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。

 1 function Product(name, price) {
 2   this.name = name;
 3   this.price = price;
 4 }
 5 
 6 function Food(name, price) {
 7   Product.call(this, name, price);
 8   this.category = 'food';
 9 }
10 
11 function Toy(name, price) {
12   Product.call(this, name, price);
13   this.category = 'toy';
14 }
15 
16 var cheese = new Food('feta', 5);
17 var fun = new Toy('robot', 40);

 

二、ES6中Class 可以通过extends关键字实现继承

http://es6.ruanyifeng.com/#docs/class-extends

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class Point {
}

class ColorPoint extends Point {
}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。

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

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

posted on 2019-10-07 14:15  songsong_p_blue  阅读(156)  评论(0编辑  收藏  举报