ES5和ES6及繼承機制

Color..「Blind」發表於2018-10-30

這幾天在學習react的時候學習到ES6的 class extends 繼承方式,就複習一下ES5的繼承機制,並整理下來。

  • ES5繼承機制
  • ES6繼承機制
  • 兩者的區別總結

ES5繼承機制

在js萬物皆物件,但事還是區分普通物件和函式物件,那大家需要知道是隻有函式物件才有prototype屬性,但所有物件都有__proto__屬性

function A(){
}
var B = new A;
複製程式碼

那這裡就得出幾個公式:

B.__proto__== A.prototype; 
B.constructor == A;
a.prototype.constuctor = A;
複製程式碼

那這是實現繼承的基礎; 也就是說A.prototype是A的原型物件,A是建構函式,B是A的例項,原型物件(A.prototype)是 建構函式(A)的一個例項。而此時this指向是指向例項。 明白這個關係實現繼承就很簡單了!看一下下面的程式碼

原型鏈繼承

通過重寫子類的原型 等於 父類的一個例項,(父類的例項屬相變成子類的原型屬性)

function father() {
    this.faName = 'father';
}
father.prototype.getfaName = function() {
    console.log(this.faName);
};
function child() {
    this.chName = 'child';
}
child.prototype = new father();
child.prototype.constructor = child;
child.prototype.getchName = function() {
    console.log(this.chName);
};
複製程式碼

組合繼承 (比較常用,也有缺點)

通過子類的原型物件指向父類例項的方式來實現繼承,那我們不難發現原型鏈的形成是真正是靠__proto_ 而非prototype,子類的例項可以訪問父類原型上的方法,是因為子類例項通過 _proto 與父類的原型物件有連線

//先來個父類,帶些屬性
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;
複製程式碼

寄生組合繼承 (業內比較提倡的方法)

所謂寄生組合式繼承,即通過藉助建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。 其背後的基本思路是:不必為了指定子型別的原型而呼叫超型別的建構函式,我們所需要的無非就是超型別原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型。

function inserit(son, father) {
    var obj = Object.create(father.prototype);
    son.prototype = obj;
    obj.constructor = son
}

function SuperType(name,colors) {
    this.name = name;
    this.colors = colors;
}
SuperType.prototype.sayName = function () {
    return this.name;
}

function SubType(job,name,color) {
    SuperType.call(this, name,color);
    this.job = job;
}
//核心方法
inserit(SubType, SuperType);
SubType.prototype.sayjob = function () {
    return this.job;
}
var instance= new SubType("doctor","John",["red","green"]);
console.log(instance.sayjob(),instance.sayName()) //doctor,John
複製程式碼
  • [x] 原型鏈繼承缺點:父類包含引用型別的屬性,那麼子類所有例項都會共享該屬性(包含引用型別的原型屬性會被例項共享),在建立子類例項時,不能向父類的建構函式傳遞引數
  • [x] 組合繼承的缺點:兩次呼叫父類建構函式:(第一次是在建立子類原型的時候,第二次是在子類建構函式內部)子類繼承父類的屬性,一組在子類例項上,一組在子類原型上(在子類原型上建立不必要的多餘的屬性)(例項上的遮蔽原型上的同名屬性)效率低
  • [x] 組合繼承的優點:只呼叫一次父類的建構函式,避免了在子類原型上建立不必要的,多餘的屬性,原型鏈保持不變

ES5的繼承機制總結

ES5的繼承機制簡單來說就是:實質是先創造子類的例項物件this,然後再將父類的方法新增到this上面(Parent.apply(this))


ES6的繼承機制

class Point {
    constructor(x) {
        this.x = 1;
        this.p = 2;
    }
    print() {
      return this.x;
    }
}
Point.prototype.z = '4'
class ColorPoint extends Point {
    constructor(x) {
        this.color = color; // ReferenceError
        super(x, y);
        this.x = x; // 正確
    }
    m() {
     super.print();
   }
}
複製程式碼

ES6繼承是通過classextends 關鍵字來實現繼承 Point是父類,ColorPoint 是子類 通過 class 新建子類 extends繼承父類的方式實現繼承,方式比ES5簡單的多。

constructor

constructor 方法是類的建構函式,是一個預設方法,通過 new 命令建立物件例項時,自動呼叫該方法。一個類必須有 constructor 方法,如果沒有顯式定義,一個預設的 consructor 方法會被預設新增。所以即使你沒有新增建構函式,也是會有一個預設的建構函式的。一般 constructor 方法返回例項物件 this ,但是也可以指定 constructor 方法返回一個全新的物件,讓返回的例項物件不是該類的例項。

class Points {
    constructor(x) {
        this.x = 1;
        this.p = 2;
    }
    print() {
      return this.x;
    }
    statc getname(){
      return new.target.name;
    }
}
等同於:
function Points(x) {
  this.x = x;
  this.p = 2;
}
Points.prototype.print = function() {
  return '(' + this.x +')';
}
複製程式碼

也就是說constructor就代表在父類上加屬性,而在class物件加方法屬性 等於在原型上的加。而這些屬性方法 只有通過new出的例項 或者是extends 繼承出來的例項才可以獲取到 所以我們可以得到

new Points().__proto__.print() //可以呼叫到Points的print方法
new Points().x = 1 //可以呼叫到constructor 裡面的 this.x=1
複製程式碼

super

super既可以當做函式使用,也可以當做物件使用兩種使用的時候完全不一樣, 函式用時 : 在 constructor 中必須呼叫 super 方法,因為子類沒有自己的 this 物件,而是繼承父類的 this 物件,然後對其進行加工,而 super 就代表了父類的建構函式。super 雖然代表了父類 A 的建構函式,但是返回的是子類 B 的例項,即 super 內部的 this 指的是 B,因此 super() 在這裡相當於

A.prototype.constructor.call(this, props)
複製程式碼

在 super() 執行時,它指向的是 子類 B 的建構函式,而不是父類 A 的建構函式。也就是說,super() 內部的 this 指向的是 B。所以在第一個es6的例子中子類的this指向的是自己。、

當做物件使用 : 在普通方法中,指向父類的原型物件;在靜態方法中,指向父類。 所以在子類的方法中super.print();指向的是父類原型上的方法。 但是因為super的兩種用法,所以es6規定在使用必須要明確使用方式,像單獨console.log(super) 就會報錯。

static

顧名思義是靜態方法的意思,類相當於例項的原型, 所有在類中定義的方法, 都會被例項繼承。 如果在一個方法前, 加上static關鍵字, 就表示該方法不會被例項繼承, 而是直接通過類來呼叫, 這就稱為“ 靜態方法”。靜態方法呼叫直接在類上進行,而在類的例項上不可被呼叫。靜態方法通常用於建立 實用/工具 函式。

new.target

new.target屬性允許你檢測函式或構造方法是否通過是通過new運算子被呼叫的。在通過new運算子被初始化的函式或構造方法中,new.target返回一個指向構造方法或函式的引用。在普通的函式呼叫中,new.target 的值是undefined。

也就是說new.target的功能就是用來檢測函式的呼叫是不是通過 new 去建立一個新物件的,而且new.target返回的是一個指向函式的引用,也就是說我們能夠確定是哪個函式進行了new操作。

ES6的繼承機制總結

先建立父類例項this 通過class丶extends丶super關鍵字定義子類,並改變this指向,super本身是指向父類的建構函式但做函式呼叫後返回的是子類的例項,實際上做了父類.prototype.constructor.call(this),做物件呼叫時指向父類.prototype,從而實現繼承。

相關文章