ES5和ES6及繼承機制


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

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

ES5繼承機制

在js萬物皆對象,但事仍是區分普通對象和函數對象,那你們須要知道是隻有函數對象纔有prototype屬性,但全部對象都有__proto__屬性react

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

那這裏就得出幾個公式:es6

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

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

原型鏈繼承

經過重寫子類的原型 等於 父類的一個實例,(父類的實例屬相變成子類的原型屬性)函數

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))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簡單的多。es5

constructor

constructor 方法是類的構造函數,是一個默認方法,經過 new 命令建立對象實例時,自動調用該方法。一個類必須有 constructor 方法,若是沒有顯式定義,一個默認的 consructor 方法會被默認添加。因此即便你沒有添加構造函數,也是會有一個默認的構造函數的。通常 constructor 方法返回實例對象 this ,可是也能夠指定 constructor 方法返回一個全新的對象,讓返回的實例對象不是該類的實例。spa

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,從而實現繼承。

相關文章
相關標籤/搜索