Babel下的ES6兼容性與規範

ES6標準發佈後,前端人員也開發漸漸瞭解到了es6,可是因爲兼容性的問題,仍然沒有獲得普遍的推廣,不過業界也用了一些折中性的方案來解決兼容性和開發體系問題,但你們仍很疑惑,使用ES6會有哪些兼容性問題。javascript

1、兼容性問題現狀

  針對ES6的新特性兼容性問題,目前解決的方案是使用語法解析轉換工具將es6語法轉化爲大部分瀏覽器識別的es5語法,通用的轉化方案有babel,traceur,jsx,typescript,es6-shim。固然es6在服務器端也存在兼容性問題,這裏因爲只考慮到瀏覽器端的開發,暫不討論。下面有一些經常使用的解決方案和兼容es6的兼容性比較~html

https://github.com/babel/babel/issues/596前端

http://kangax.github.io/compat-table/es6/java

考慮到解決方案的多樣性,我麼會着重考慮如下幾個問題: 1,兼容性:是否能解決目前常見的全部語法兼容轉換 2,易用性:可以很方便的接入到現有的開發構建體系中 3,通用性:業界有較大承認,目前沒有很大問題 4,持續性:有較權威的團隊維護,並能不斷更新react

  綜合四點,咱們仍然考慮使用babel做爲咱們的兼容性解決方案。即便之後須要更換,只須要更換更換工具就能夠了,原有代碼的寫法能夠不動。   除了後面三點,咱們可能比較關注babel處理es6時的兼容性問題。由於es6裏面增長了較多的內容,轉換爲es5沒有對應語法與之對應,因此使用時要尤其注意。爲此也沒有很好的方法進行判斷,只能對於es6裏的新增內容進行編譯,判斷是否能夠轉爲es5語法。webpack

ES6新特性在Babel下的兼容性列表git

ES6特性 兼容性
箭頭函數 支持
類的聲明和繼承 部分支持,IE8不支持
加強的對象字面量 支持
字符串模板 支持
解構 支持,但注意使用方式
參數默認值,不定參數,拓展參數 支持
let與const 支持
for of IE不支持
iterator, generator 不支持
模塊 module、Proxies、Symbol 不支持
Map,Set 和 WeakMap,WeakSet 不支持
Promises、Math,Number,String,Object 的新API 不支持
export & import 支持
生成器函數 不支持
數組拷貝 支持
在es6的新特性中,複雜結構的仍然不支持對es5轉換的兼容,具體兼容性能夠從下面實例來看~

1.1 箭頭操做符

箭頭操做符能夠簡潔的描述一個函數es6

// ES6
var fn= (v=>console.log(v));

轉換後github

// ES6
"use strict";

var fn = function fn(v) {
  return console.log(v);
};

該用法能夠放心使用。web

1.2 類的聲明和繼承

//類的定義
class Animal {
    //ES6中新型構造器
    constructor(name) {
        this.name = name;
    }
    //實例方法
    sayName() {
        console.log('My name is '+this.name);
    }
}
//類的繼承
class Programmer extends Animal {
    constructor(name) {
        //直接調用父類構造器進行初始化
        super(name);
    }
    program() {
        console.log("I'm coding...");
    }
}
//測試咱們的類
var animal=new Animal('dummy'),
wayou=new Programmer('wayou');
animal.sayName();//輸出 ‘My name is dummy’
wayou.sayName();//輸出 ‘My name is wayou’
wayou.program();//輸出 ‘I'm coding...’

轉換後

'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 Animal = (function () {
    //ES6中新型構造器
 
    function Animal(name) {
        _classCallCheck(this, Animal);
 
        this.name = name;
    }
 
    //類的繼承
 
    //實例方法
 
    _createClass(Animal, [{
        key: 'sayName',
        value: function sayName() {
            console.log('My name is ' + this.name);
        }
    }]);
 
    return Animal;
})();
 
var Programmer = (function (_Animal) {
    _inherits(Programmer, _Animal);
 
    function Programmer(name) {
        _classCallCheck(this, Programmer);
 
        //直接調用父類構造器進行初始化
        _get(Object.getPrototypeOf(Programmer.prototype), 'constructor', this).call(this, name);
    }
 
    //測試咱們的類
 
    _createClass(Programmer, [{
        key: 'program',
        value: function program() {
            console.log("I'm coding...");
        }
    }]);
 
    return Programmer;
})(Animal);
 
var animal = new Animal('dummy'),
    wayou = new Programmer('wayou');
animal.sayName(); //輸出 ‘My name is dummy’
wayou.sayName(); //輸出 ‘My name is wayou’
wayou.program(); //輸出 ‘I'm coding...’

轉換過程使用了Object.defineProperty,在ie8下不兼容,除此外能夠任意使用

1.3 加強的對象字面量

//經過對象字面量建立對象
var human = {
    breathe() {
        console.log('breathing...');
    }
};
var worker = {
    __proto__: human, //設置此對象的原型爲human,至關於繼承human
    company: 'freelancer',
    work() {
        console.log('working...');
    }
};
human.breathe();//輸出 ‘breathing...’
//調用繼承來的breathe方法
worker.breathe();//輸出 ‘breathing...’

轉換後

//經過對象字面量建立對象
'use strict';
 
var human = {
    breathe: function breathe() {
        console.log('breathing...');
    }
};
var worker = {
    __proto__: human, //設置此對象的原型爲human,至關於繼承human
    company: 'freelancer',
    work: function work() {
        console.log('working...');
    }
};
human.breathe(); //輸出 ‘breathing...’
//調用繼承來的breathe方法
worker.breathe(); //輸出 ‘breathing...’

這個能夠任意使用

1.4 字符串模板

//產生一個隨機數
var num=Math.random();
console.log(`your num is ${num}`);

轉換後

//產生一個隨機數
"use strict";

var num = Math.random();
console.log("your num is " + num);

1.5 解構

var [name,gender,age]=['wayou','male','secrect'];//數組解構
console.log('name:'+name+', age:'+age);//輸出: name:wayou, age:secrect

轉化後

'use strict';
 
var name = 'wayou';
var gender = 'male';
var age = 'secrect';
//數組解構
console.log('name:' + name + ', age:' + age); //輸出: name:wayou, age:secrect

此方法可使用。可是儘可能不要使用 var [a, b] = getVal(); 的方式,儘管getVal返回一個數組。由於此時會用到isArray,IE8上不能支持。

1.6 參數默認值,不定參數,拓展參數

  • 參數默認值
function sayHello(age, name='dude'){
    console.log(`Hello ${name}`);
}
sayHello(12);

轉換後

'use strict';
 
function sayHello(age) {
    var name = arguments.length <= 1 || arguments[1] === undefined ? 'dude' : arguments[1];
 
    console.log('Hello ' + name);
}
sayHello(12);
  • 不定參數
//將全部參數相加的函數
function add(...x){
    return x.reduce((m,n)=>m+n);
}
//傳遞任意個數的參數
console.log(add(1,2,3));//輸出:6
console.log(add(1,2,3,4,5));//輸出:15

轉換後

//將全部參數相加的函數
"use strict";
 
function add() {
    for (var _len = arguments.length, x = Array(_len), _key = 0; _key < _len; _key++) {
        x[_key] = arguments[_key];
    }
 
    return x.reduce(function (m, n) {
        return m + n;
    });
}
//傳遞任意個數的參數
console.log(add(1, 2, 3)); //輸出:6
console.log(add(1, 2, 3, 4, 5)); //輸出:15
  • 擴展參數
var people=['Wayou','John','Sherlock'];
//sayHello函數原本接收三個單獨的參數人妖,人二和人三
function sayHello(people1,people2,people3){
    console.log(`Hello ${people1},${people2},${people3}`);
}
//可是咱們將一個數組以拓展參數的形式傳遞,它能很好地映射到每一個單獨的參數
sayHello(...people);//輸出:Hello Wayou,John,Sherlock
 
//而在之前,若是須要傳遞數組當參數,咱們須要使用函數的apply方法
sayHello.apply(null,people);//輸出:Hello Wayou,John,Sherlock

轉換後

'use strict';
 
var people = ['Wayou', 'John', 'Sherlock'];
//sayHello函數原本接收三個單獨的參數人妖,人二和人三
function sayHello(people1, people2, people3) {
    console.log('Hello ' + people1 + ',' + people2 + ',' + people3);
}
//可是咱們將一個數組以拓展參數的形式傳遞,它能很好地映射到每一個單獨的參數
sayHello.apply(undefined, people); //輸出:Hello Wayou,John,Sherlock
 
//而在之前,若是須要傳遞數組當參數,咱們須要使用函數的apply方法
sayHello.apply(null, people); //輸出:Hello Wayou,John,Sherlock

參數默認值,不定參數,拓展參數均可以徹底使用

1.7 let與const

let和const徹底支持,將都會被轉爲var,可是要理解let、var、const的區別。

1.8 for of

var someArray = [ "a", "b", "c" ];
 
for (v of someArray) {
    console.log(v);//輸出 a,b,c
}

轉換後

"use strict";
 
var someArray = ["a", "b", "c"];
 
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
 
try {
  for (var _iterator = someArray[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    v = _step.value;
 
    console.log(v); //輸出 a,b,c
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator["return"]) {
      _iterator["return"]();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

這裏IE下面沒有throw,因此沒法支持

1.9 iterator, generator

var ids = {
  *[Symbol.iterator]: function () {
    var index = 0;
 
    return {
      next: function () {
        return { value: 'id-' + index++, done: false };
      }
    };
  }
};```

轉換後

```javascript

'use strict';
 
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 ids = _defineProperty({}, Symbol.iterator, function () {
  var index = 0;
 
  return {
    next: function next() {
      return { value: 'id-' + index++, done: false };
    }
  };
});

不建議使用,轉換後仍須要瀏覽器支持

1.10 模塊 module、Proxies、Symbol

// point.js
module "point" {
    export class Point {
        constructor (x, y) {
            public x = x;
            public y = y;
        }
    }
}

徹底不支持,import也不支持,解析報錯,因此建議不使用,使用原來的require

1.11 Map,Set 和 WeakMap,WeakSet

Map,Set 和 WeakMap,WeakSet在es5中都沒有對應的類型與之對應,因此均不支持轉換,由瀏覽器決定兼容性

1.12 Promises、Math,Number,String,Object 的新API

不作語法轉換,由瀏覽器決定兼容性

1.13 export & import

export function myModule(someArg) {
  return someArg;
}```

轉換後

```javascript
"use strict";
 
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.myModule = myModule;
 
function myModule(someArg) {
  return someArg;
}
import * as baz from 'myModule';

轉換後

'use strict';
 
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } 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 _myModule = require('myModule');
 
var baz = _interopRequireWildcard(_myModule);

因此可使用export和import等方法來進行模塊的加載處理依賴,同時export使用到了defineProperty,IE8兼容性存在問題。

1.14 生成器函數

function* foo() { };
var bar = foo();
bar.next(); // Object {value: undefined, done: true}

轉換後

"use strict";
 
var marked0$0 = [foo].map(regeneratorRuntime.mark);
function foo() {
  return regeneratorRuntime.wrap(function foo$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
      case "end":
        return context$1$0.stop();
    }
  }, marked0$0[0], this);
};
var bar = foo();
bar.next(); // Object {value: undefined, done: true}

regeneratorRuntime在IE下面不能支持,因此不建議使用。

  ES6新特性用到的就這些,其它的基本由瀏覽器自己決定。這部分代碼Babel會像處理es5代碼同樣,不進行加工處理。對於部分ES6的語法,Babel會解析拋錯,即便不拋錯也不進行處理,建議不使用。 1.15 數組拷貝

const items = [1,2,3];
const itemsCopy = [...items];

轉換後

"use strict";

var items = [1, 2, 3];
var itemsCopy = [].concat(items);

可使用

2、ES6打包體系

  使用babel處理了一部分ES6的兼容性轉換問題,可是ES6的打包依然必須依賴目前通用的打包管理方案。目前流行的打包方案有如下幾種 1,webpack+babel+browsify+gulp gulp負責構建、使用webpack打包、browsify管理組件,babel轉換 這目前被說的最多的解決方案,由於github上一大堆例子,並且能夠很簡單的支持到react,可是這樣不能造成一個完整的開發體系;另外過於統一,和所在團隊使用的技術差別較大。

2,fis3 + babel + Qjs + lego 選擇這樣的方案一方面是由於團隊目前的主要技術選型,另外一方面則是每一個工具的特有優點。目前這塊仍在不斷完善當中。

3、小結

  因此使用ES6這一方案來進行實際開發是否有必定的必要性仍須要進行考慮,由於es6的高等特性在es5中沒有對應的特性去代替,即便可以代替也是使用一些複雜的自定義函數去作,而部分可轉換實現的特性仍然較少,並且寫起來確實很簡潔,這也是es6的一大優點。

  另外爲了讓Babel能在實際的開發中使用,咱們也本身總結了一份關於ES6部分的規範:

  https://github.com/ouvens/ecmaScript-2015-babel-rules

原文出處:https://ouvens.github.io/frontend-javascript/2015/10/16/es6-under-babel.html

相關文章
相關標籤/搜索