国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

ES6 探秘:Classes

caoym / 786人閱讀

摘要:中的同名的實(shí)際上就是我們?cè)诘脑屠^承中使用的構(gòu)造函數(shù),所以中的是對(duì)中的構(gòu)造函數(shù)的一種包裝。我們發(fā)現(xiàn),在中設(shè)定的屬性被放在的構(gòu)造函數(shù)中,而方法則以鍵值對(duì)的形式傳入一個(gè)函數(shù)中。大家是不是對(duì)這種繼承模式似曾相識(shí)呢對(duì)了,這就是所謂的構(gòu)造函數(shù)竊取。

ES6中增加了一些新特性,但從底層的角度來(lái)說(shuō),只是一些語(yǔ)法糖。但是就我個(gè)人來(lái)說(shuō),如果不了解這些語(yǔ)法糖的本質(zhì),是用不安心的。那我們要如何揭開(kāi)這些語(yǔ)法糖的真實(shí)面目呢?

Babel to the rescue! Babel是一款將ES6代碼轉(zhuǎn)換為ES5代碼的編譯器,從而讓我們可以無(wú)視瀏覽器的支持,直接享受ES6的新特性。同時(shí),我們也可以通過(guò)研究Babel編譯出的ES5代碼,來(lái)揭開(kāi)ES6的面紗。

ES6 Classes

ES6中的Classes是在Javascript現(xiàn)有的原型繼承的基礎(chǔ)上引入的一種語(yǔ)法糖。Class語(yǔ)法并沒(méi)有引入一種新的繼承模式。它為對(duì)象創(chuàng)建和繼承提供了更清晰,易用的語(yǔ)法。

我們用class關(guān)鍵字來(lái)創(chuàng)建一個(gè)類(lèi),constructor關(guān)鍵字定義構(gòu)造函數(shù),用extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承,super來(lái)實(shí)現(xiàn)調(diào)用父類(lèi)方法。

好,下面是一個(gè)ES6 class語(yǔ)法的完整例子:

//定義父類(lèi)View
class View {
  constructor(options) {
    this.model = options.model;
    this.template = options.template;
  }

  render() {
    return _.template(this.template, this.model.toObject());
  }
}
//實(shí)例化父類(lèi)View
var view = new View({
  template: "Hello, <%= name %>"
});
//定義子類(lèi)LogView,繼承父類(lèi)View
class LogView extends View {
  render() {
    var compiled = super.render();
    console.log(compiled);
  }
}

這段簡(jiǎn)短的代碼就用到了上述的幾個(gè)關(guān)鍵詞。class語(yǔ)法的確的簡(jiǎn)潔明確,借鑒了主流OO語(yǔ)言的語(yǔ)法,更易于理解。

然而我在用這段代碼時(shí),又有些猶豫。這還是我熟悉的js原型繼承嗎,這真的是同一種繼承模式的一個(gè)語(yǔ)法糖嗎?

真相究竟是如何呢?我們就拿babel編譯之后的代碼作為切入口,來(lái)看看ES6 class語(yǔ)法的本質(zhì)。

下面是上述ES6代碼用babel編譯之后的結(jié)果:

"use strict";
var _get = function get(_x, _x2, _x3) {
    var _again = true;
    _function: while (_again) {
        var object = _x,
            property = _x2,
            receiver = _x3;
        desc = parent = getter = undefined;
        _again = false;
        if (object === null) object = Function.prototype;
        var desc = Object.getOwnPropertyDescriptor(object, property);
        if (desc === undefined) {
            var parent = Object.getPrototypeOf(object);
            if (parent === null) {
                return undefined;
            } else {
                _x = parent;
                _x2 = property;
                _x3 = receiver;
                _again = true;
                continue _function;
            }
        } else if ("value" in desc) {
            return desc.value;
        } else {
            var getter = desc.get;
            if (getter === undefined) {
                return undefined;
            }
            return getter.call(receiver);
        }
    }
};

var _createClass = (function() {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };
})();

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

var View = (function() {
    function View(options) {
        _classCallCheck(this, View);
            this.model = options.model;
        this.template = options.template;
    }
    _createClass(View, [{
        key: "render",
        value: function render() {
            return _.template(this.template, this.model.toObject());
        }
    }]);
    return View;
})();

var LogView = (function(_View) {
    _inherits(LogView, _View);
    function LogView() {
        _classCallCheck(this, LogView);
        _get(Object.getPrototypeOf(LogView.prototype), "constructor", this).apply(this, arguments);
    }
    _createClass(LogView, [{
        key: "render",
        value: function render() {
            var compiled = _get(Object.getPrototypeOf(LogView.prototype), "render", this).call(this);
            console.log(compiled);
        }
    }]);
    return LogView;  
    
})(View);

這段代碼很長(zhǎng),我們只關(guān)注里面的函數(shù),可以得到它的結(jié)構(gòu)如下:

//用于得到原型鏈上屬性的方法的函數(shù)
var _get = function get(_x, _x2, _x3) {
    //······
}

//用于創(chuàng)建對(duì)象的函數(shù)
var _createClass = (function() {
     //內(nèi)部函數(shù),定義對(duì)象的屬性
    function defineProperties(target, props) {
        //······
    }
    return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };
})();

//用于實(shí)現(xiàn)繼承的函數(shù)
function _inherits(subClass, superClass) {
        //······
        subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
});

var View = (function() {
    //······
    return View;
})();

var LogView = (function(_View) {
    //······
})(View);
View類(lèi)的實(shí)現(xiàn)

我們從一個(gè)View類(lèi)的創(chuàng)建開(kāi)始分析

class View {
  constructor(options) {
    this.model = options.model;
    this.template = options.template;
  }
  render() {
    return _.template(this.template, this.model.toObject());
  }
}

//ES5代碼
var View = (function() {
    function View(options) {
        _classCallCheck(this, View);
            this.model = options.model;
        this.template = options.template;
    }
    _createClass(View, [{
        key: "render",
        value: function render() {
            return _.template(this.template, this.model.toObject());
        }
    }]);
    return View;
})();

我們從編譯之后的代碼中可以看出,View是一個(gè)IIFE,里面是一個(gè)同名的函數(shù)View,這個(gè)函數(shù)經(jīng)過(guò) _createClass()函數(shù)的處理之后,被返回了。所以我們得出的第一點(diǎn)結(jié)論就是,ES6中的class實(shí)際就是函數(shù)。當(dāng)然這點(diǎn)在各種文檔中已經(jīng)明確了,所以讓我們繼續(xù)分析。

IIFE中的同名的View實(shí)際上就是我們?cè)贓S5的原型繼承中使用的構(gòu)造函數(shù),所以ES6中的class是對(duì)ES5中的構(gòu)造函數(shù)的一種包裝。我們發(fā)現(xiàn),在class中設(shè)定的屬性被放在ES5的構(gòu)造函數(shù)中,而方法則以鍵值對(duì)的形式傳入一個(gè)_createClass()函數(shù)中。那么這個(gè)_createClass()函數(shù)又制造了什么魔法呢?

var _createClass = (function() {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };
})();

_createClass也是一個(gè)IIFE,有一個(gè)內(nèi)部的函數(shù)defineProperties,這個(gè)函數(shù)遍歷屬性的描述符,進(jìn)行描述符的默認(rèn)設(shè)置,最后使用Object.defineProperty()方法來(lái)寫(xiě)入對(duì)象的屬性。IIFE的renturn部分有兩個(gè)分支,一個(gè)是針對(duì)一個(gè)類(lèi)的原型鏈方法,一個(gè)是靜態(tài)方法,我們看到原型鏈方法被寫(xiě)入構(gòu)造函數(shù)的原型對(duì)象里,而靜態(tài)方法則被直接寫(xiě)入構(gòu)造函數(shù)里,因此我們不用實(shí)例化對(duì)象就可以直接調(diào)用一個(gè)類(lèi)的靜態(tài)方法了

js中的函數(shù)是對(duì)象,F(xiàn)unction構(gòu)造函數(shù)的prototype指向Object.prototype,因此可以寫(xiě)入屬性

類(lèi)繼承的實(shí)現(xiàn)

OK,到目前我們已經(jīng)搞清了ES6的class關(guān)鍵字是如何工作的,那么ES6中的繼承有是如何實(shí)現(xiàn)的呢?下面讓我們看看_inherits()函數(shù)。

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

_inherits()函數(shù)的關(guān)鍵部分便是subClass.prototype = Object.create(···)。通過(guò)Object.create()方法來(lái)指定新創(chuàng)建對(duì)象的原型,由此省去了對(duì)父類(lèi)構(gòu)造函數(shù)的處理,達(dá)到了簡(jiǎn)單的原型繼承效果。

然后我們來(lái)看看創(chuàng)建LogView類(lèi)的代碼:

//ES6
class LogView extends View {
  render() {
    var compiled = super.render();
    console.log(compiled);
  }
}
//ES5
var LogView = (function(_View) {
    _inherits(LogView, _View);
    function LogView() {
        _classCallCheck(this, LogView);
        _get(Object.getPrototypeOf(LogView.prototype), "constructor", this).apply(this, arguments);
    }
    _createClass(LogView, [{
        key: "render",
        value: function render() {
            var compiled = _get(Object.getPrototypeOf(LogView.prototype), "render", this).call(this);
            console.log(compiled);
        }
    }]);
    return LogView;
})(View);

LogView類(lèi)和View類(lèi)的最大不同便是增加了一個(gè)_get()函數(shù)的調(diào)用,我們仔細(xì)看這個(gè)_get()函數(shù)會(huì)發(fā)現(xiàn)它接收幾個(gè)參數(shù),子類(lèi)的原型,"constructor"標(biāo)識(shí)符,還有this。再看下面對(duì)super.render()的處理,同樣是用_get()函數(shù)來(lái)處理的。再看_get()函數(shù)的源碼,就可以發(fā)現(xiàn)_get()函數(shù)的作用便是遍歷對(duì)象的原型鏈,找出傳入的標(biāo)識(shí)符對(duì)應(yīng)的屬性,把它用apply綁定在當(dāng)前上下文上執(zhí)行。

大家是不是對(duì)這種繼承模式似曾相識(shí)呢?對(duì)了,這就是所謂的“構(gòu)造函數(shù)竊取”。當(dāng)我們需要繼承父類(lèi)的屬性時(shí),在子類(lèi)的構(gòu)造函數(shù)內(nèi)部調(diào)用父類(lèi)構(gòu)造函數(shù),在加上Object.create()大法,然后就可以繼承父類(lèi)的所有屬性和方法了。

結(jié)語(yǔ)

以上就是筆者對(duì)ES6中的class做的一些解讀,依據(jù)是babel的編譯結(jié)果。今天剛好看到getify大神的一篇博客,將的是ES6中的箭頭函數(shù)其實(shí)并不是一個(gè)語(yǔ)法糖,而是一種新的不帶this的函數(shù)。他還進(jìn)一步說(shuō)了,錯(cuò)誤的過(guò)程得到正確的結(jié)果,會(huì)導(dǎo)致很多后續(xù)的問(wèn)題。

而本文的結(jié)論是建立在class是一種語(yǔ)法糖的基礎(chǔ)之上的,根據(jù)MDN的文檔,我們應(yīng)該可以確認(rèn)這個(gè)結(jié)論是可靠的。而ES6中的特性,如箭頭函數(shù),并不都是語(yǔ)法糖,因此在后續(xù)的ES6探秘文章中,我們將用其他的途徑,來(lái)揭示ES6種種神奇魔法的秘密。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/78626.html

相關(guān)文章

  • SpringBoot 中 @SpringBootApplication注解背后的三體結(jié)構(gòu)探秘

    摘要:概述約定大于配置的功力讓我們?nèi)玢宕猴L(fēng),在我之前寫(xiě)的文章從到也對(duì)比過(guò)和這兩個(gè)框架,不過(guò)最終以超高的代碼信噪比和易上手性讓我們映像頗深。至于,我想在非時(shí)代大家應(yīng)該不陌生吧,作用是配置容器,也即形式的容器的配置類(lèi)所使用。 showImg(https://segmentfault.com/img/remote/1460000015822144); 概 述 SpringBoot 約定大于配置...

    Tecode 評(píng)論0 收藏0
  • ES6引入前需要解決的問(wèn)題

    摘要:的新特性哪些適合使用我們參考使用進(jìn)行開(kāi)發(fā)的思考文章推薦的新特性,僅使用三星的。另外推薦閱讀探秘系列的新特性是否通過(guò)轉(zhuǎn)換后還有兼容問(wèn)題團(tuán)隊(duì)中又同學(xué)正在驗(yàn)證,我們驗(yàn)證的環(huán)境是,我們最終會(huì)使用三星特性加上兼容性的。 showImg(https://segmentfault.com/img/bVrjev); 最近項(xiàng)目中的一個(gè)模塊正式引入的ES6,由于是引入新技術(shù),也遇到了一些問(wèn)題,下面分享下整...

    verano 評(píng)論0 收藏0
  • redux中間件探秘

    摘要:接下來(lái)我們來(lái)看看源碼中的模塊是怎么應(yīng)用中間件的。如何實(shí)現(xiàn)中間件操作的。新的會(huì)從第一個(gè)中間件開(kāi)始觸發(fā),這樣,在我們調(diào)用的時(shí)候,就會(huì)將中間件走一遍了。函數(shù)如果存在多個(gè)中間件,直接使用方法將各個(gè)中間件嵌套起來(lái)。 從redux-thunk引出思考 在使用redux-thunk進(jìn)行異步action書(shū)寫(xiě)的時(shí)候,我經(jīng)常好奇redux到底如何運(yùn)作,讓asyncAction成為可能 為了探究,我們必須看...

    Jeff 評(píng)論0 收藏0
  • JavaScript 工作原理之十五-類(lèi)和繼承及 Babel 和 TypeScript 代碼轉(zhuǎn)換探秘

    摘要:使用新的易用的類(lèi)定義,歸根結(jié)底也是要?jiǎng)?chuàng)建構(gòu)造函數(shù)和修改原型。首先,它把構(gòu)造函數(shù)當(dāng)成單獨(dú)的函數(shù)且包含類(lèi)屬性集。該節(jié)點(diǎn)還儲(chǔ)存了指向父類(lèi)的指針引用,該父類(lèi)也并儲(chǔ)存了構(gòu)造函數(shù),屬性集和及父類(lèi)引用,依次類(lèi)推。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第...

    GeekGhc 評(píng)論0 收藏0
  • JavaScript 工作原理之十五-類(lèi)和繼承及 Babel 和 TypeScript 代碼轉(zhuǎn)換探秘

    摘要:使用新的易用的類(lèi)定義,歸根結(jié)底也是要?jiǎng)?chuàng)建構(gòu)造函數(shù)和修改原型。首先,它把構(gòu)造函數(shù)當(dāng)成單獨(dú)的函數(shù)且包含類(lèi)屬性集。該節(jié)點(diǎn)還儲(chǔ)存了指向父類(lèi)的指針引用,該父類(lèi)也并儲(chǔ)存了構(gòu)造函數(shù),屬性集和及父類(lèi)引用,依次類(lèi)推。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第...

    BigNerdCoding 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<