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
複製代碼

待補充。。。

相關文章
相關標籤/搜索