ES5和ES6中对继承的实现

1.原型链相关知识

  正式学习以前,咱们先温习下原型链的相关知识:(图片来源:Javascript的原型链图es6

这里写图片描述

  图片中有待补充的内容包括:每个new出来的对象或者函数有一个constructor 属性指向构造函数,例子以下:bash

var b = new Function();
b.constructor === Function;  //true
复制代码

  先对照图片对下面的例子作出解释app

var a = {};
console.log(a.prototype);  //=> undefined (实例化出来的对象)

var b = function(){};
console.log(b.prototype);  //=> {} (对应Foo.prototype)

var c = 'Hello';
console.log(c.prototype);  //=> undefined (实例出来的字符串)
复制代码

补充知识点:函数

var o1 = new Object();
typeof(o1);      //=> 'object'

var o2 = {};
typeof(o2);     //=> 'object'

typeof(Object); //=> 'function'
复制代码

  咱们不可贵出一个结论,typeof所的到的对象类型和对象的__proto__类型相同,Function.prototype除外。学习

typeof Function.prototype.__proto__ ;  //=> 'object'
复制代码

2.ES5中的继承

(相关阅读:ES5和ES6中的继承/ECMAScript 继承机制实现ui

2.1 原型链方法

将父类实例当作子类构造函数的原型this

//先来个父类,带些属性
function Super(){
    this.flag = true;
}
//为了提升复用性,方法绑定在父类原型属性上
Super.prototype.getFlag = function(){
    return this.flag;
}
//来个子类
function Sub(){
    this.subFlag = false;
}
//实现继承
Sub.prototype = new Super;
//给子类添加子类特有的方法,注意顺序要在继承以后
Sub.prototype.getSubFlag = function(){
    return this.subFlag;
}
//构造实例
var es5 = new Sub;
复制代码

优势:es5

  • 父类的方法(getName)获得了复用。

缺点:spa

  • 原型中属性的改变会反应到全部的实例上
  • 建立子类的实例时,不能向父类的构造函数传递参数

  例子以下:prototype

function Super(){
    this.flag = true;
}//父类
function Sub(){
   this.subFlag = false;
}//子类
Sub.prototype = new Super();//继承
var obj = new Sub();//子类实例1
obj.flag = flase;  //修改以后,因为是原型上的属性,以后建立的全部实例都会受到影响
var obj_2 = new Sub();//子类实例2
console.log(obj.flag)  //false复制代码

2.2 call和apply方法(和对象冒充相似)

call方法,它的第一个参数用做 this 的对象。其余参数都直接传递给函数自身。

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};

var obj = new Object();
obj.color = "blue";

sayColor.call(obj, "The color is ", "a very nice color indeed.");
复制代码

  在这个例子中,函数 sayColor()在对象外定义,即便它不属于任何对象,也能够引用关键字 this。对象 objcolor属性等于blue。调用call() 方法时,第一个参数是 obj,说明应该赋予sayColor() 函数中的 this 关键字值是 obj。第二个和第三个参数是字符串。它们与 sayColor() 函数中的参数sPrefixsSuffix 匹配,最后生成的消息 "The color is blue, a very nice color indeed." 将被显示出来。

优势:

  • 子类的每一个实例都有本身的属性(name),不会相互影响。

缺点:

  • 父类方法没有获得复用,方法存在于每一个实例当中而不是来自继承。

2.3 混合方式

function A (color){
	this.color = color
}
A.prototype.sayColor = function(){
	console.log(this.color)
}
function B(sColor,name){
	A.call(this,sColor);
	this.name = name;
}
B.prototype = new A();
B.prototype.sayName = function(){
	console.log(this.name)
}
var b = new B("red","nik")
复制代码

请注意:
在执行 B.prototype = new A(); 以后,B.prototype.constructor 指向A 。然而constructor的定义是要指向原型属性对应的构造函数的,B.prototypeB构造函数的原型,因此应该添加一句纠正:

B.prototype.constructor = B;
复制代码

2 ES6中的继承

  相关阅读(Class的继承

2.1 简介

  Class能够经过extends关键字实现继承,这比 ES5 的经过修改原型链实现继承,要清晰和方便不少。

class Point {
}

class ColorPoint extends Point {
}
复制代码

   以上代码实际等同于下面的代码:

class ColorPoint extends Point {
}

// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}
复制代码

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

  子类必须在constructor方法中调用super方法,不然新建实例时会报错。这是由于子类没有本身的this对象,而是继承父类的this对象,而后对其进行加工。若是不调用super方法,子类就得不到this对象。

  **注意:**在子类的构造函数中,只有调用super以后,才可使用this关键字,不然会报错。这是由于子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

2.2 super 关键字

  super这个关键字,既能够看成函数使用,也能够看成对象使用。在这两种状况下,它的用法彻底不一样。

  第一种状况,super做为函数调用时,表明父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

  super虽然表明了父类A的构造函数,可是返回的是子类B的实例,即super内部的this指的是B,所以super()在这里至关于A.prototype.constructor.call(this)

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B
复制代码

  上面代码中,new.target指向当前正在执行的函数。能够看到,在super()执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()内部的this指向的是B

  第二种状况,super做为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();
复制代码

  上面代码中,子类B当中的super.p(),就是将super看成一个对象使用。这时,super在普通方法之中,指向A.prototype,因此super.p()就至关于A.prototype.p()

  因为super指向父类的原型对象,因此定义在父类实例上的方法或属性,是没法经过super调用的。

  以下:

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined
复制代码

  上面代码中,p是父类A实例的属性,super.p就引用不到它。

  若是属性定义在父类的原型对象上,super就能够取到。以下:

class A {}
A.prototype.x = 2;

class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}

let b = new B();
复制代码

ES6 规定,经过super调用父类的方法时,方法内部的this指向子类。

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2
复制代码

待补充。。。

相关文章
相关标签/搜索