工做中有不少用到Class類的地方,一般是暴露出class類,或者暴露Class類的實例,好比商品有價格、規格、名稱等屬性,就能夠經過new一個Class類來初始化商品對象。程序員
對es6有了解的朋友應該知道es6是es5的語法糖,Class類其實是基於es5的構造函數實現的生成實例對象的方法,可是Class類的寫法更優雅,更趨近於傳統的面向對象編程。es6
雖然說平時經常使用Class類,但是也常會忽略掉細節而致使問題,出於好奇和爲了完全理解Class類,我利用babel工具降級es6語法,看看Class類的「廬山真面目」,以及理解babel到底作了什麼,寫文記錄以供本身複習。編程
定義一個Class類數組
class K { constructor(name) { this.name = name; } // 靜態方法 static classMethod() { this.getname() return 'hello'; } // setter set prop(value) { console.log('setter: '+ value); } // getter get prop() { return 'getter'; } // 原型方法 getName() { return "celeste"; } } let k = new K("celeste")
上面的類利用babel降級語法後代碼以下:babel
var K = function () { function K(name) { _classCallCheck(this, K); this.name = name; } _createClass(K, [{ key: 'getName', value: function getname() { return "celeste"; } }, { key: 'prop', set: function set(value) { console.log('setter: ' + value); }, get: function get() { return 'getter'; } }], [{ key: 'classMethod', value: function classMethod() { this.getName(); return 'hello'; } }]); return K; }(); var k = new K("celeste");
能夠發現Class類本質上是個自執行函數。這個函數執行完畢返回一個構造函數K。
而且,這裏定義函數不是用函數聲明的形式,而是用變量聲明賦值var K,這其實就是class類不存在變量提高的緣由,由於雖然js函數會先掃描整個函數體語句,將全部聲明的變量提高到函數的頂部,可是不會提高賦值,在console前變量K還未賦值因此打印結果是undefined。函數
// 變量賦值 console.log(Bb); // undefined var Bb = function Bb () {}; // 函數聲明 console.log(Aa); // ƒ Aa () {} function Aa () {};
看完外層,再看看裏面的關鍵信息,主要看_classCallCheck、_createClass他們作了什麼,源碼以下:工具
"use strict"; // 爲了向前兼容,es6語法其實是嚴格模式的 // 類or模塊中只有嚴格模式可用 // 判斷right是否爲left的構造函數 function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } } // 判斷Constructor是否instance的構造函數,若是不是則拋出錯誤 function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // 遍歷props,設置props裏每一項的屬性並掛載到target上 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; // 定義是否可枚舉(否) descriptor.enumerable = descriptor.enumerable || false; // 定義是否可刪除(可) descriptor.configurable = true; // descriptor有value屬性的話(即除了set/get外的原型方法),可賦值 if ("value" in descriptor) descriptor.writable = true; // 將變量descriptor.key定義到target上 Object.defineProperty(target, descriptor.key, descriptor); } } // 參數分別是:構造函數、原型方法、靜態方法 function _createClass(Constructor, protoProps, staticProps) { // 原型方法掛載到構造函數的原型上 if (protoProps) _defineProperties(Constructor.prototype, protoProps); // 靜態方法(用了static關鍵字定義的函數)會做爲第三個參數數組裏的項傳進來,會直接成爲構造函數下的一個屬性 if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
因此constructor事實上就是初始化了一個構造函數:this
function K(name) { _classCallCheck(this, K); this.name = name; }
_classCallCheck(this, K)的做用就是判斷K是否爲this的構造函數,不是的話拋出錯誤,確保萬無一失(依據是若是K是個構造函數那麼this必定是指向K的實例對象的)。es5
而_createClass函數的做用是就是將定義在類裏的方法掛載到函數的原型(針對原型方法)或者類自己(針對static靜態方法)上:prototype
把_createClass函數與函數調用直觀地放在一塊兒看: // _createClass函數 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } // 語法降級後自執行函數裏的函數執行 _createClass(K, [{ key: 'getName', value: function getname() { return "celeste"; } }, { key: 'prop', set: function set(value) { console.log('setter: ' + value); }, get: function get() { return 'getter'; } }], [{ key: 'classMethod', value: function classMethod() { this.getName(); return 'hello'; } }]);
能夠看到,setter、getter也在第二個參數數組裏,他們也是原型上的方法,傳參時有些許不一樣,value —— set/get,是爲了在掛載到原型上的時候加以區分的,把他們區分開的代碼就是_defineProperties函數裏的這句話:
if ("value" in descriptor) descriptor.writable = true;
1.類的全部方法都定義在類的prototype
屬性上面
因此類的新方法能夠利用`Object.assign`添加在`prototype`對象上面 Object.assign(Person.prototype, { // add some functions ... });
2.類的內部全部定義的方法,都是不可枚舉的(non-enumerable)
3.js引擎會自動爲空的類添加一個空的constructor
方法(事實上就是會默認建立一個構造函數,將構造函數的this指向類的實例)
4.constructor
函數能夠return Object.create(null)返回一個全新的對象,可致使實例對象不是類的實例。
下一節再看剩餘的問題啦,這兩天總結完畢或許會合併成一篇也可能新開一篇...——2020/06/06 01:20
小貼士:語法糖(Syntactic sugar),也譯爲糖衣語法,是由英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能並無影響,可是更方便程序員使用。一般來講使用語法糖可以增長程序的可讀性,從而減小程序代碼出錯的機會。
babel在線工具 https://babeljs.io/repl