js中class是怎麼實現的

class的實現


        一談到編程,咱們首先想到的就是O(Object)O(Oriented)P(Programming)編程,也就是面向對象編程。面向對象編程是一種設計思想。若是把程序當作一我的,那麼對象就是各個器官,對象裏面的各類操做函數就是細胞。javascript

        不少語言中面向對象的藍圖都是基於類,好比Pytho、C++、Java。js在es6語法中也引入了類的概念,不過仍是基於es5的原型鏈實現的語法糖。而爲何js一開始並無用類的概念呢?這都是由於Brendan Eich一開始設計js的時候只是想設計出一門運行在瀏覽器上能解決他手頭上的需求的語言,因此他沒有引入類的概念,而是用原型模式來實現js的繼承。java

        既然談到原型,那確定你們都對prototype不陌生,這個屬性實際是一個指針,指向一個對象,而這個對象包含有特定類型的全部實例共享的屬性和方法。講人話就是:「這個傢伙的一些東西,他的兒子孫子也都能同時擁有。這些東西是共享的。」而class就是基於這個原型實現的,至於怎麼實現的,咱們能夠看看代碼:es6

//class
class Hello {
    constructor(x) {
        this.x = x;
    }
    greet() {
      console.log("Hello, " + this.x)
    }
}

let world = new Hello("world");
world.greet()

//es5
var Hello = (function () {
    function Hello(x) {
        this.x = x;
    }
    Hello.prototype.greet = function () {
        console.log("Hello, " + this.x);
    };
    return Hello;
}());
var world = new Hello("world");
world.greet();

複製代碼

上述的例子很簡單地詮釋了class的語法在es5中是怎麼實現的,有意思的是class的constructor其實就是扮演了構造函數的角色,本質上仍是利用構造函數和原型模式來實現class的繼承。類的實例調用方法,實際上就是調用原型上的方法。(這裏要注意,類的內部默認就是嚴格模式,因此不須要使用use strict指定運行模式)編程

原型對象


es5語法

讓咱們先打印出es5語法實現的world來看看:設計模式

var Hello = (function () {
    function Hello(x) {
        this.x = x;
    }
    Hello.prototype.greet = function () {
        console.log("Hello, " + this.x);
    };
    return Hello;
}());
var world = new Hello("world");

console.log(world)
複製代碼

對js原型有所瞭解的朋友都知道 world.__proto__指向的是world繼承的原型對象,有意思的是 world.__proto__.constructor指回了Hello構造函數,然而根據原型模式 Hello.prototype === world.__proto__,也就是說 Hello.prototype.constructor也是指回了Hello構造函數。讓咱們用來代碼來展現:
var Hello = (function () {
    function Hello(x) {
        this.x = x;
    }
    Hello.prototype.greet = function () {
        console.log("Hello, " + this.x);
    };
    return Hello;
}());

var world = new Hello("world");

world.__proto__.constructor === Hello  //true
world.__proto__ === Hello.prototype   //true
Hello.prototype.constructor === Hello  //true
複製代碼

class語法

再讓咱們來打印出class實現的world看看:瀏覽器

class Hello {
    constructor(x) {
        this.x = x;
    }
    greet() {
      console.log("Hello, " + this.x)
    }
}

let world = new Hello("world");

console.log(world)
複製代碼

上圖和es5語法打印出來的基本同樣,只不過constructor指向的不是Hello構造函數而是Hello類。也就是說:
class Hello {
    constructor(x) {
        this.x = x;
    }
    greet() {
      console.log("Hello, " + this.x)
    }
}

let world = new Hello("world");

world.__proto__.constructor === Hello  //true
world.__proto__ === Hello.prototype   //true
Hello.prototype.constructor === Hello  //true
複製代碼

看完上面的解析,充分證實class確實是es5的語法糖,class的繼承仍是基於原型鏈,因此仍是建議你們認真去了解js的原型模式和原型鏈,才能充分理解js的class,不過在實際工做中仍是建議使用class,這樣會更直觀,代碼也方便維護。函數

靜態方法


類就是實例的原型,全部在類中定義的方法,都會被實例繼承,而類的靜態方法不會被實例繼承,那麼是怎麼實現的呢?ui

// class
class Hello {
    constructor(x) {
        this.x = x;
    }
    static greet() {
      console.log("Hello, world")
    }
}

// es5
var Hello = (function () {
    function Hello(x) {
        this.x = x;
    }
    Hello.greet = function () {
        console.log("Hello, world");
    };
    return Hello;
}());

複製代碼

我相信有些人看到這的反應就是:this

就這樣來實現靜態方法?這麼簡單粗暴?其實我以爲還好,很直觀明瞭。

繼承


class經過關鍵字extends來實現繼承,這也是比es5經過原型鏈實現繼承的一個優點:清晰方便。那麼咱們來看看es5語法如何實現繼承:es5

//class
class Hello {
    constructor(x) {
        this.x = x;
    }
    static greet() {
      console.log("Hello, world")
    }
}

class Hi extends Hello {
    constructor(x) {
        super(x)
    }
}

// es5
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Hello = (function () {
    function Hello(x) {
        this.x = x;
    }
    Hello.greet = function () {
        console.log("Hello, world");
    };
    return Hello;
}());
var Hi = (function (_super) {
    __extends(Hi, _super);
    function Hi(x) {
        return _super.call(this, x) || this;
    }
    return Hi;
}(Hello));

複製代碼

還記得阮一峯的《ECMAScript 6 入門》的有關super的介紹嗎?

子類必須在constructor方法中調用super方法,不然新建實例時會報錯。這是由於子類本身的this對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super方法,子類就得不到this對象。

上面的代碼能夠充分詮釋阮老師的這幾句話,其實最後仍是利用call函數來完成實例this的綁定。

結語


以上只是大概講述一下class是怎麼實現的,其實還有不少屬性和方法是更騷地操做,之後有時間我會寫出跟你們分享。建議你們仍是深刻理解js的原型模式和原型鏈,畢竟不論出es六、es7語法,這些都是基於js的基本設計模式實現的。

相關文章
相關標籤/搜索