摘要:大概就是將對象里面的一些屬性轉換成數組,方便解構賦值的進行。而則更貼近的寫法,性能更好一些,兼容性更好一些,但將這部份代碼再轉換成的話會比較麻煩一些感覺這一點并不是缺點,有源碼就可以了。上面解決的辦法,實質就是將改成。
前言原文鏈接:https://github.com/lcxfs1991/blog/issues/9
將babel捧作前端一個劃時代的工具一定也不為過,它的出現讓許多程序員幸福地用上了es6新語法。但你就這么放心地讓babel跑在外網?反正我是不放心,我就曾經過被坑過,于是萌生了研究babel代碼轉換的想法。本文不是分析babel源碼,僅僅是看看babel轉換的最終產物。
es6在babel中又稱為es2015。由于es2015語法眾多,本文僅挑選了較為常用的一些語法點,而且主要是分析babel-preset-2015這個插件(react開發的時候,常在webpack中用到這個preset)。
babel-preset-2015打開babel-preset2015插件一看,一共20個插件。熟悉es2015語法的同志一看,多多少少能從字面意思知道某個插件是用于哪種語法的轉換
babel-plugin-transform-es2015-template-literals => es2015模板
babel-plugin-transform-es2015-literals
babel-plugin-transform-es2015-function-name => 函數name屬性
babel-plugin-transform-es2015-arrow-functions => 箭頭函數
babel-plugin-transform-es2015-block-scoped-functions => 函數塊級作用域
babel-plugin-transform-es2015-classes => class類
babel-plugin-transform-es2015-object-super => super提供了調用prototype的方式
babel-plugin-transform-es2015-shorthand-properties => 對象屬性的快捷定義,如obj = { x, y }
babel-plugin-transform-es2015-computed-properties => 對象中括號屬性,如obj = {["x]: 1}
babel-plugin-transform-es2015-for-of => 對象for of遍歷
babel-plugin-transform-es2015-sticky-regex
babel-plugin-transform-es2015-unicode-regex
babel-plugin-check-es2015-constants => const常量
babel-plugin-transform-es2015-spread => 對象擴展運算符屬性,如...foobar
babel-plugin-transform-es2015-parameters => 函數參數默認值及擴展運算符
babel-plugin-transform-es2015-destructuring => 賦值解構
babel-plugin-transform-es2015-block-scoping => let和const塊級作用域
babel-plugin-transform-es2015-typeof-symbol => symbol特性
babel-plugin-transform-es2015-modules-commonjs => commonjs模塊加載
babel-plugin-transform-regenerator => generator特性
var, const and letconst和let現在一律轉換成var。那const到底如何保證不變呢?如果你在源碼中第二次修改const常量的值,babel編譯會直接報錯。
轉換前
var a = 1; let b = 2; const c = 3;
轉換后:
var a = 1; var b = 2; var c = 3;
那let的塊級作用怎么體現呢?來看看下面例子,實質就是在塊級作用改變一下變量名,使之與外層不同。
轉換前:
let a1 = 1; let a2 = 6; { let a1 = 2; let a2 = 5; { let a1 = 4; let a2 = 5; } } a1 = 3;
轉換后:
var a1 = 1; var a2 = 6; { var _a = 2; var _a2 = 5; { var _a3 = 4; var _a4 = 5; } } a1 = 3;賦值解構
寫react的時候,我們使用負值解構去取對象的值,用起來非常爽,像這樣:
var props = { name: "heyli", getName: function() { }, setName: function() { } }; let { name, getName, setName } = this.props;
我們來看看轉換的結果:
var props = { name: "heyli", getName: function getName() {}, setName: function setName() {} }; var name = props.name; var getName = props.getName; var setName = props.setName;
至于數組呢?如果是一個匿名數組,則babel會幫你先定義一個變量存放這個數組,然后再對需要賦值的變量進行賦值。
轉換前:
var [ a1, a2 ] = [1, 2, 3];
轉換后:
var _ref = [1, 2, 3]; var a1 = _ref[0]; var a2 = _ref[1];
看到這個,感覺轉換結果跟我們想的還蠻一致。哈哈,使用的噩夢還沒開始。
如果使用匿名對象直接進行賦值解構會怎樣呢?如下。babel為了使接收的變量唯一,直接就將匿名對象里的屬性拼在一起,組成接收這個匿名對象的變量,嚇得我趕緊檢查一下項目里有沒有這種寫法。
轉換前:
var { abc, bcd, cde, def } = { "abc": "abc", "bcd": "bcd", "cde": "cde", "def": "def", "efg": "efg", "fgh": "fgh" };
轉換后:
var _abc$bcd$cde$def$efg$ = { "abc": "abc", "bcd": "bcd", "cde": "cde", "def": "def", "efg": "efg", "fgh": "fgh" }; var abc = _abc$bcd$cde$def$efg$.abc; var bcd = _abc$bcd$cde$def$efg$.bcd; var cde = _abc$bcd$cde$def$efg$.cde; var def = _abc$bcd$cde$def$efg$.def;
還有一種對象深層次的解構賦值:
轉換前:
var obj = { p1: [ "Hello", { p2: "World" } ] }; var { p1: [s1, { p2 }] } = obj;
轉換后:
// 為解釋本人將代碼美化了 var _slicedToArray = (function() { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { // 用Symbol.iterator造了一個可遍歷對象,然后進去遍歷。 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; })(); var obj = { p1: ["Hello", { p2: "World" }] }; var _obj$p = _slicedToArray(obj.p1, 2); var s1 = _obj$p[0]; var p2 = _obj$p[1].p2;
babel在代碼頂部生產了一個公共的代碼_slicedToArray。大概就是將對象里面的一些屬性轉換成數組,方便解構賦值的進行。但Symbol.iterator的兼容性并不好(如下圖),還是謹慎使用為妙。
另外,下面這種對字符串進行賦值解構也同樣使用到_slicedToArray方法:
const [a, b, c, d, e] = "hello";函數參數默認值及擴展運算符
在es5的年代,一般我們寫參數的默認值都會這么寫:
function func(x, y) { var x = x || 1; var y = y || 2; }
我們來看看babel的轉換辦法:
function func({x, y} = { x: 0, y: 0 }) { return [x, y]; } function func1(x = 1, y = 2) { return [x, y]; }
function func() { var _ref = arguments.length <= 0 || arguments[0] === undefined ? { x: 0, y: 0 } : arguments[0]; var x = _ref.x; var y = _ref.y; return [x, y]; } function func1() { var x = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0]; var y = arguments.length <= 1 || arguments[1] === undefined ? 2 : arguments[1]; return [x, y]; }
babel這里使有了arguments來做判。第一種情況涉及解構賦值,因此x和y的值還是有可能是undefined的。至于第二種情況,則會保證2個參數的默認值分別是1和2.
再來看一種。...y代表它接收了剩下的參數。也就是arguments除了第一個標號的參數之外剩余的參數。
轉換前:
function func(x, ...y) { console.log(x); console.log(y); return x * y.length; }
轉換后:
function func(x) { console.log(x); for (var _len = arguments.length, y = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { y[_key - 1] = arguments[_key]; } console.log(y); return x * y.length; }箭頭函數
剪頭函數其實主要是省了寫函數的代碼,同時能夠直接用使外層的this而不用擔心context切換的問題。以前我們一般都要在外層多寫一個_this/self直向this。babel的轉換辦法其實跟我們的處理無異。
轉換前:
var obj = { prop: 1, func: function() { var _this = this; var innerFunc = () => { this.prop = 1; }; var innerFunc1 = function() { this.prop = 1; }; }, };
轉換后:
var obj = { prop: 1, func: function func() { var _this2 = this; var _this = this; var innerFunc = function innerFunc() { _this2.prop = 1; }; var innerFunc1 = function innerFunc1() { this.prop = 1; }; } };對象的能力增強 對象屬性的快捷定義
轉換前:
var a = 1, b = "2", c = function() { console.log("c"); }; var obj = {a, b, c};
轉換后:
var a = 1, b = "2", c = function c() { console.log("c"); }; var obj = { a: a, b: b, c: c };對象中括號屬性
es2015開始新增了在對象中用中括號解釋屬性的功能,這對變量、常量等當對象屬性尤其有用。
轉換前:
const prop2 = "PROP2"; var obj = { ["prop"]: 1, ["func"]: function() { console.log("func"); }, [prop2]: 3 };
轉換后:
var _obj; // 已美化 function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var prop2 = "PROP2"; var obj = (_obj = {}, _defineProperty(_obj, "prop", 1), _defineProperty(_obj, "func", function func() { console.log("func"); }), _defineProperty(_obj, prop2, 3), _obj);
看似簡單的屬性,babel卻大動干戈。新增了一個_defineProperty函數,給新建的_obj = {}進行屬性定義。除此之外使用小括號包住一系列從左到右的運算使整個定義更簡潔。
使用super去調用prototype以前我們一般都用obj.prototype或者嘗試用this去往上尋找prototype上面的方法。而babel則自己寫了一套在prototype鏈上尋找方法/屬性的算法。
轉換前:
var obj = { toString() { // Super calls return "d " + super.toString(); }, };
轉換后:
var _obj; // 已美化 var _get = function get(object, property, receiver) { // 如果prototype為空,則往Function的prototype上尋找 if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); // 如果在本層prototype找不到,再往更深層的prototype上找 if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } // 如果是屬性,則直接返回 else if ("value" in desc) { return desc.value; } // 如果是方法,則用call來調用,receiver是調用的對象 else { var getter = desc.get; // getOwnPropertyDescriptor返回的getter方法 if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var obj = _obj = { toString: function toString() { // Super calls return "d " + _get(Object.getPrototypeOf(_obj), "toString", this).call(this); } };Object.assign 和 Object.is
es6新增的Object.assign極大方便了對象的克隆復制。但babel的es2015 preset并不支持,所以沒對其進入轉換,這會使得一些移動端機子遇到這種寫法會報錯。所以一般開發者都會使用object-assign這個npm的庫做兼容。
Object.is用于比較對象的值與類型,es2015 preset同樣不支持編譯。
es6模板 多行字符串轉換前:
console.log(`string text line 1 string text line 2`);
轉換后:
console.log("string text line 1 string text line 2");字符中變量運算
轉換前:
var a = 5; var b = 10; console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);
轉換后:
var a = 5; var b = 10; console.log("Fifteen is " + (a + b) + " and not " + (2 * a + b) + ".");標簽模板
es6的這種新特性給模板處理賦予更強大的功能,一改以往對模板進行各種replace的處理辦法,用一個統一的handler去處理。babel的轉換主要是添加了2個屬性,因此看起來也并不算比較工程浩大的編譯。
轉換前:
var a = 5; var b = 10; function tag(strings, ...values) { console.log(strings[0]); // "Hello " console.log(strings[1]); // " world " console.log(values[0]); // 15 console.log(values[1]); // 50 return "Bazinga!"; } tag`Hello ${ a + b } world ${ a * b }`;
轉換后:
var _templateObject = _taggedTemplateLiteral(["Hello ", " world ", ""], ["Hello ", " world ", ""]); // 已美化 function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } // 給傳入的object定義strings和raw兩個不可變的屬性。 var a = 5; var b = 10; function tag(strings) { console.log(strings[0]); // "Hello " console.log(strings[1]); // " world " console.log(arguments.length <= 1 ? undefined : arguments[1]); // 15 console.log(arguments.length <= 2 ? undefined : arguments[2]); // 50 return "Bazinga!"; } tag(_templateObject, a + b, a * b);模塊化與類 類class
javascript實現oo一直是非常熱門的話題。從最原始時代需要手動維護在構造函數里調用父類構造函數,到后來封裝好函數進行extend繼承,再到babel出現之后可以像其它面向對象的語言一樣直接寫class。es2015的類方案仍然算是過渡方案,它所支持的特性仍然沒有涵蓋類的所有特性。目前主要支持的有:
constructor
static方法
get 方法
set 方法
類繼承
super調用父類方法。
轉換前:
class Animal { constructor(name, type) { this.name = name; this.type = type; } walk() { console.log("walk"); } run() { console.log("run") } static getType() { return this.type; } get getName() { return this.name; } set setName(name) { this.name = name; } } class Dog extends Animal { constructor(name, type) { super(name, type); } get getName() { return super.getName(); } }
轉換后(由于代碼太長,先省略輔助的方法):
/** ......一堆輔助方法,后文詳述 **/ var Animal = (function () { function Animal(name, type) { // 此處是constructor的實現,用_classCallCheck來判定constructor正確與否 _classCallCheck(this, Animal); this.name = name; this.type = type; } // _creatClass用于創建類及其對應的方法 _createClass(Animal, [{ key: "walk", value: function walk() { console.log("walk"); } }, { key: "run", value: function run() { console.log("run"); } }, { key: "getName", get: function get() { return this.name; } }, { key: "setName", set: function set(name) { this.name = name; } }], [{ key: "getType", value: function getType() { return this.type; } }]); return Animal; })(); var Dog = (function (_Animal) { // 子類繼承父類 _inherits(Dog, _Animal); function Dog(name, type) { _classCallCheck(this, Dog); // 子類實現constructor // babel會強制子類在constructor中使用super,否則編譯會報錯 return _possibleConstructorReturn(this, Object.getPrototypeOf(Dog).call(this, name, type)); } _createClass(Dog, [{ key: "getName", get: function get() { // 跟上文使用super調用原型鏈的super編譯解析的方法一致, // 也是自己寫了一個回溯prototype原型鏈 return _get(Object.getPrototypeOf(Dog.prototype), "getName", this).call(this); } }]); return Dog; })(Animal);
// 檢測constructor正確與否 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
// 創建類 var _createClass = (function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; // es6規范要求類方法為non-enumerable descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; // 對于setter和getter方法,writable為false if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { // 非靜態方法定義在原型鏈上 if (protoProps) defineProperties(Constructor.prototype, protoProps); // 靜態方法直接定義在constructor函數上 if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
// 繼承類 function _inherits(subClass, superClass) { // 父類一定要是function類型 if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } // 使原型鏈subClass.prototype.__proto__指向父類superClass,同時保證constructor是subClass自己 subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); // 保證subClass.__proto__指向父類superClass if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// 子類實現constructor function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn"t been initialised - super() hasn"t been called"); } // 若call是函數/對象則返回 return call && (typeof call === "object" || typeof call === "function") ? call : self; }
先前在用react重構項目的時候,所有的react組件都已經摒棄了es5的寫法,一律采用了es6。用類的好處寫繼續更加方便,但無法用mixin,需要借助更新的es7語法中的decorator才能夠實現類mixin的功能(例如pureRender)。但這次分析完babel源碼之后,才發現原來babel在實現class特性的時候,定義了許多方法,盡管看起來并不太優雅。
模塊化在開發react的時候,我們往往用webpack搭配babel的es2015和react兩個preset進行構建。之前看了一篇文章對babel此處的模塊加載有些啟發(《分析 Babel 轉換 ES6 module 的原理》)。
示例:
// test.js import { Animal as Ani, catwalk } from "./t1"; import * as All from "./t2"; class Cat extends Ani { constructor() { super(); } } class Dog extends Ani { constructor() { super(); } }
// t1.js export class Animal { constructor() { } } export function catwal() { console.log("cat walk"); };
// t2.js export class Person { constructor() { } } export class Plane { constructor() { } }
通過webpack與babel編譯后:
// t1.js的模塊 Object.defineProperty(exports, "__esModule", { value: true }); exports.catwal = catwal; // 省略一些類繼承的方法 var Animal = exports.Animal = function Animal() { _classCallCheck(this, Animal); }; function catwal() { console.log("cat walk"); }; // t2.js的模塊 Object.defineProperty(exports, "__esModule", { value: true }); // 省略一些類繼承的方法 var Person = exports.Person = function Person() { _classCallCheck(this, Person); }; var Plane = exports.Plane = function Plane() { _classCallCheck(this, Plane); }; // test.js的模塊 var _t = __webpack_require__(1); var _t2 = __webpack_require__(3); // 返回的都是exports上返回的對象屬性 var All = _interopRequireWildcard(_t2); function _interopRequireWildcard(obj) { // 發現是babel編譯的, 直接返回 if (obj && obj.__esModule) { return obj; } // 非babel編譯, 猜測可能是第三方模塊,為了不報錯,讓default指向它自己 else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } // 省略一些類繼承的方法 var Cat = (function (_Ani) { _inherits(Cat, _Ani); function Cat() { _classCallCheck(this, Cat); return _possibleConstructorReturn(this, Object.getPrototypeOf(Cat).call(this)); } return Cat; })(_t.Animal); var Dog = (function (_Ani2) { _inherits(Dog, _Ani2); function Dog() { _classCallCheck(this, Dog); return _possibleConstructorReturn(this, Object.getPrototypeOf(Dog).call(this)); } return Dog; })(_t.Animal);
es6的模塊加載是屬于多對象多加載,而commonjs則屬于單對象單加載。babel需要做一些手腳才能將es6的模塊寫法寫成commonjs的寫法。主要是通過定義__esModule這個屬性來判斷這個模塊是否經過babel的編譯。然后通過_interopRequireWildcard對各個模塊的引用進行相應的處理。
另一個發現是,通過webpack打包babel編譯后的代碼,每一個模塊里面都包含了相同的類繼承幫助方法,這是開發時忽略的。由此可看,在開發react的時候用es5的語法可能會比使用es6的class能使js bundle更小。
babel es2015 loose mode開發家校群的時候,在android4.0下面報esModule錯誤的問題,如下:
Uncaught TypeError: Cannot assign to read only property "__esModule" of #。
經查證,發現是構建中babel-es2015 loader的模式問題,會導致Android4.0的用戶有報錯。只需要使用loose mode就可以解決問題。下面是相關的stackoverflow issue以及對應解決問題的npm包。
stackoverflow
babel-preset-est2015-loose npm package
那么es2015和normal mode和loose mode有什么區別呢,這個出名的博客略有介紹:Babel 6: loose mode。
實質就是(作者總結)normal mode的轉換更貼近es6的寫法,許多的property都是通過Object.defineProperty進行的。而loose mode則更貼近es5的寫法,性能更好一些,兼容性更好一些,但將這部份代碼再轉換成native es6的話會比較麻煩一些(感覺這一點并不是缺點,有源碼就可以了)。
上面esModule解決的辦法,實質就是將
Object.defineProperty(exports, "__esModule", { value: true });
改成 exports.__esModule = true;。
再舉個例子,如下面的Cat類定義:
class Cat extends Ani { constructor() { super(); } miao() { console.log("miao"); } }
正常模式會編譯為:
var Cat = (function (_Ani) { _inherits(Cat, _Ani); function Cat() { _classCallCheck(this, Cat); return _possibleConstructorReturn(this, Object.getPrototypeOf(Cat).call(this)); } _createClass(Cat, [{ key: "miao", value: function miao() { console.log("miao"); } }]); return Cat; })(_t.Animal);
loose mode模式會編譯為:
var Cat = (function (_Ani) { _inherits(Cat, _Ani); function Cat() { _classCallCheck(this, Cat); return _possibleConstructorReturn(this, _Ani.call(this)); } Cat.prototype.miao = function miao() { console.log("miao"); }; return Cat; })(_t.Animal);
babel es2015中loose模式主要是針對下面幾個plugin:
transform-es2015-template-literals
transform-es2015-classes
transform-es2015-computed-properties
transform-es2015-for-of
transform-es2015-spread
transform-es2015-destructuring
transform-es2015-modules-commonjs
每一種的轉換方式在此就不再贅述了,大家可以回家自己試。
如有錯誤,懇請斧正!
參考文章 babel try outhttps://babeljs.io/repl/
template literalshttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
block-scoped functionshttps://blogs.msdn.microsoft.com/cdndevs/2015/09/16/future-of-javascript-ecmascript-6-es2015-block-scoping/
http://www.programmerinterview.com/index.php/javascript/javascript-block-scope/
http://www.2ality.com/2015/02/es6-scoping.html
classeshttp://purplebamboo.github.io/2014/07/13/javascript-oo-class/
http://blog.rainy.im/2015/07/20/prototype-chain-in-js/
objectshttp://fourkitchens.com/blog/article/practical-introduction-es2015-objects
commonjs and es6 modulehttp://ryerh.com/javascript/2016/03/27/babel-module-implementation.html
http://www.2ality.com/2015/12/babel6-loose-mode.html
http://www.2ality.com/2015/02/es6-classes-final.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79432.html
摘要:是目前最常用的轉的工具,但即使是,各瀏覽器的支持度也是不一的,因此便產生了本文,找出一些能夠被翻譯成兼容性高代碼的語法。不可用因為會使用到參考資料,用來查瀏覽器兼容性。到底將代碼轉換成什么鳥樣,感謝。官網試驗轉換后的代碼 前言 由于目前各瀏覽器對ES6兼容性較低,再加上需要兼容歷史上各種版本的瀏覽器,因此,使用編譯器將ES6語法轉譯成ES5語法則勢在必行了。babel是目前最常用的ES...
摘要:因此,你還是需要各種各樣雜七雜八的工具來轉換你的代碼噢,我可去你媽的吧,這些東西都是干嘛的我就是想用個模塊化,我到底該用啥子本文正旨在列出幾種可用的在生產環境中放心使用模塊化的方法,希望能幫到諸位后來者這方面的中文資源實在是忒少了。 原文發表在我的博客上。最近搗鼓了一下 ES6 的模塊化,分享一些經驗 :) Python3 已經發布了九年了,Python 社區卻還在用 Python 2...
摘要:所以這是一篇插隊的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據入參返回具體的描述符。其次局部來看,裝飾器具體應用表達式是,其函數簽名和是一模一樣。等裝飾器語法,是和直接使用是等效等價的。 ================前言=================== 初衷:以系列故事的方式展現 MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解...
摘要:正常情況,的返回值就是一個對象,其實也就是對象。好了,上面算是基本說清楚了使用語法定義類繼承類,到底發生了什么,如果錯誤,還請指正,謝謝 自從有了webpack之后,我們這些jscoder似乎得到了前所未有的解放,箭頭函數,對象解構,let,const關鍵字,以及class、extends等等關鍵字使用得不亦樂乎,反正,webpack會幫我們把這些es6代碼轉換成瀏覽器能夠識別的es5...
閱讀 2179·2021-11-24 09:39
閱讀 2793·2021-07-29 13:49
閱讀 2327·2019-08-29 14:15
閱讀 2240·2019-08-29 12:40
閱讀 3318·2019-08-26 13:42
閱讀 638·2019-08-26 12:13
閱讀 2073·2019-08-26 11:41
閱讀 3352·2019-08-23 18:32