摘要:前言在了解是如何編譯前,我們先看看的和的構造函數是如何對應的。這是它跟普通構造函數的一個主要區別,后者不用也可以執行。該函數的作用就是將函數數組中的方法添加到構造函數或者構造函數的原型中,最后返回這個構造函數。
前言
在了解 Babel 是如何編譯 class 前,我們先看看 ES6 的 class 和 ES5 的構造函數是如何對應的。畢竟,ES6 的 class 可以看作一個語法糖,它的絕大部分功能,ES5 都可以做到,新的 class 寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。
constructorES6 中:
class Person { constructor(name) { this.name = name; } sayHello() { return "hello, I am " + this.name; } } var kevin = new Person("Kevin"); kevin.sayHello(); // hello, I am Kevin
對應到 ES5 中就是:
function Person(name) { this.name = name; } Person.prototype.sayHello = function () { return "hello, I am " + this.name; }; var kevin = new Person("Kevin"); kevin.sayHello(); // hello, I am Kevin
我們可以看到 ES5 的構造函數 Person,對應 ES6 的 Person 類的 constructor 方法。
值得注意的是:類的內部所有定義的方法,都是不可枚舉的(non-enumerable)
以上面的例子為例,在 ES6 中:
Object.keys(Person.prototype); // [] Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]
然而在 ES5 中:
Object.keys(Person.prototype); // ["sayHello"] Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]實例屬性
以前,我們定義實例屬性,只能寫在類的 constructor 方法里面。比如:
class Person { constructor() { this.state = { count: 0 }; } }
然而現在有一個提案,對實例屬性和靜態屬性都規定了新的寫法,而且 Babel 已經支持。現在我們可以寫成:
class Person { state = { count: 0 }; }
對應到 ES5 都是:
function Person() { this.state = { count: 0 }; }靜態方法
所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上 static 關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱為“靜態方法”。
ES6 中:
class Person { static sayHello() { return "hello"; } } Person.sayHello() // "hello" var kevin = new Person(); kevin.sayHello(); // TypeError: kevin.sayHello is not a function
對應 ES5:
function Person() {} Person.sayHello = function() { return "hello"; }; Person.sayHello(); // "hello" var kevin = new Person(); kevin.sayHello(); // TypeError: kevin.sayHello is not a function靜態屬性
靜態屬性指的是 Class 本身的屬性,即 Class.propName,而不是定義在實例對象(this)上的屬性。以前,我們添加靜態屬性只可以這樣:
class Person {} Person.name = "kevin";
因為上面提到的提案,現在可以寫成:
class Person { static name = "kevin"; }
對應到 ES5 都是:
function Person() {}; Person.name = "kevin";new 調用
值得注意的是:類必須使用 new 調用,否則會報錯。這是它跟普通構造函數的一個主要區別,后者不用 new 也可以執行。
class Person {} Person(); // TypeError: Class constructor Foo cannot be invoked without "new"getter 和 setter
與 ES5 一樣,在“類”的內部可以使用 get 和 set 關鍵字,對某個屬性設置存值函數和取值函數,攔截該屬性的存取行為。
class Person { get name() { return "kevin"; } set name(newName) { console.log("new name 為:" + newName) } } let person = new Person(); person.name = "daisy"; // new name 為:daisy console.log(person.name); // kevin
對應到 ES5 中:
function Person(name) {} Person.prototype = { get name() { return "kevin"; }, set name(newName) { console.log("new name 為:" + newName) } } let person = new Person(); person.name = "daisy"; // new name 為:daisy console.log(person.name); // kevinBabel 編譯
至此,我們已經知道了有關“類”的方法中,ES6 與 ES5 是如何對應的,實際上 Babel 在編譯時并不會直接就轉成這種形式,Babel 會自己生成一些輔助函數,幫助實現 ES6 的特性。
我們可以在 Babel 官網的 Try it out 頁面查看 ES6 的代碼編譯成什么樣子。
編譯(一)ES6 代碼為:
class Person { constructor(name) { this.name = name; } }
Babel 編譯為:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person(name) { _classCallCheck(this, Person); this.name = name; };
_classCallCheck 的作用是檢查 Person 是否是通過 new 的方式調用,在上面,我們也說過,類必須使用 new 調用,否則會報錯。
當我們使用 var person = Person() 的形式調用的時候,this 指向 window,所以 instance instanceof Constructor 就會為 false,與 ES6 的要求一致。
編譯(二)ES6 代碼為:
class Person { // 實例屬性 foo = "foo"; // 靜態屬性 static bar = "bar"; constructor(name) { this.name = name; } }
Babel 編譯為:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person(name) { _classCallCheck(this, Person); this.foo = "foo"; this.name = name; }; Person.bar = "bar";編譯(三)
ES6 代碼為:
class Person { constructor(name) { this.name = name; } sayHello() { return "hello, I am " + this.name; } static onlySayHello() { return "hello" } get name() { return "kevin"; } set name(newName) { console.log("new name 為:" + newName) } }
對應到 ES5 的代碼應該是:
function Person(name) { this.name = name; } Person.prototype = { sayHello: function () { return "hello, I am " + this.name; }, get name() { return "kevin"; }, set name(newName) { console.log("new name 為:" + newName) } } Person.onlySayHello = function () { return "hello" };
Babel 編譯后為:
"use strict"; 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function() { function Person(name) { _classCallCheck(this, Person); this.name = name; } _createClass(Person, [{ key: "sayHello", value: function sayHello() { return "hello, I am " + this.name; } }, { key: "name", get: function get() { return "kevin"; }, set: function set(newName) { console.log("new name 為:" + newName); } }], [{ key: "onlySayHello", value: function onlySayHello() { return "hello"; } }]); return Person; }();
我們可以看到 Babel 生成了一個 _createClass 輔助函數,該函數傳入三個參數,第一個是構造函數,在這個例子中也就是 Person,第二個是要添加到原型上的函數數組,第三個是要添加到構造函數本身的函數數組,也就是所有添加 static 關鍵字的函數。該函數的作用就是將函數數組中的方法添加到構造函數或者構造函數的原型中,最后返回這個構造函數。
在其中,又生成了一個 defineProperties 輔助函數,使用 Object.defineProperty 方法添加屬性。
默認 enumerable 為 false,configurable 為 true,這個在上面也有強調過,是為了防止 Object.keys() 之類的方法遍歷到。然后通過判斷 value 是否存在,來判斷是否是 getter 和 setter。如果存在 value,就為 descriptor 添加 value 和 writable 屬性,如果不存在,就直接使用 get 和 set 屬性。
寫在后面至此,我們已經了解了 Babel 是如何編譯一個 Class 的,然而,Class 還有一個重要的特性就是繼承,Class 如何繼承,Babel 又該如何編譯,歡迎期待下一篇《 ES6 系列之 Babel 是如何編譯 Class 的(下)》
ES6 系列ES6 系列目錄地址:https://github.com/mqyqingfeng/Blog
ES6 系列預計寫二十篇左右,旨在加深 ES6 部分知識點的理解,重點講解塊級作用域、標簽模板、箭頭函數、Symbol、Set、Map 以及 Promise 的模擬實現、模塊加載方案、異步處理等內容。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎 star,對作者也是一種鼓勵。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99014.html
摘要:以上的代碼對應到就是調用父類的值得注意的是關鍵字表示父類的構造函數,相當于的。舉個例子這是因為作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。 前言 在上一篇 《 ES6 系列 Babel 是如何編譯 Class 的(上)》,我們知道了 Babel 是如何編譯 Class 的,這篇我們學習 Babel 是如何用 ES5 實現...
摘要:并且用驗證了中一系列的實質就是魔法糖的本質。抽絲剝繭我們首先看的編譯結果這是一個自執行函數,它接受一個參數就是他要繼承的父類,返回一個構造函數。 如果你已經看過第一篇揭秘babel的魔法之class魔法處理,這篇將會是一個延伸;如果你還沒看過,并且也不想現在就去讀一下,單獨看這篇也沒有關系,并不存在理解上的障礙。 上一篇針對Babel對ES6里面基礎class的編譯進行了分析。這一篇將...
摘要:年,很多人已經開始接觸環境,并且早已經用在了生產當中。我們發現,關鍵字會被編譯成構造函數,于是我們便可以通過來實現實例的生成。下一篇文章我會繼續介紹如何處理子類的并會通過一段函數橋梁,使得環境下也能夠繼承定義的。 2017年,很多人已經開始接觸ES6環境,并且早已經用在了生產當中。我們知道ES6在大部分瀏覽器還是跑不通的,因此我們使用了偉大的Babel來進行編譯。很多人可能沒有關心過,...
摘要:第二部分源碼解析接下是應用多個第二部分對于一個方法應用了多個,比如會編譯為在第二部分的源碼中,執行了和操作,由此我們也可以發現,如果同一個方法有多個裝飾器,會由內向外執行。有了裝飾器,就可以改寫上面的代碼。 Decorator 裝飾器主要用于: 裝飾類 裝飾方法或屬性 裝飾類 @annotation class MyClass { } function annotation(ta...
閱讀 2556·2021-09-22 15:25
閱讀 2963·2021-09-14 18:03
閱讀 1212·2021-09-09 09:33
閱讀 1700·2021-09-07 09:59
閱讀 2930·2021-07-29 13:50
閱讀 1500·2019-08-30 15:44
閱讀 1715·2019-08-29 16:22
閱讀 1287·2019-08-29 12:49