Cho-Ching's Blog

[ES6] ECMAScript 6 筆記(一) -- Arrows, Classes, Template Strings, Destructing, let, const

Learn ES2015的翻譯與筆記。

利用Babel的REPL可以很快測試看出ES5與ES6語法差異。

CLI

使用babel的CLI工具來測試

$  npm install --g babel

監看測試的檔案並且重新編譯:

$ babel script.js --watch -o script_compiled.js

或是直接使用io.js注意,不是所有的ES6特性 iojs都實作好了, 請參考ECMAScript 6 compatibility table

Arrows

=>箭頭函數表示式 (Arrow function expression,又叫 fat arrow function) 比起一般的函數表示式語法更短以及詞彙上綁定 this 變數, 所有的箭頭函數都是無名函數 (anonymous function).

ES6 code:

var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

相當於ES5 code:

var odds = evens.map(function (v) {
  return v + 1;
});

var nums = evens.map(function (v, i) {
  return v + i;
});

使用=>讓程式看起來簡潔多了!

ES6 code:

var bob = {
  _name: "Bob",
  _friends: ['aaa','bbb','ccc'],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
}

ES5函數的this變數在不同狀況下一直指向不同值, 現在箭頭函數會自動將 this 變數綁定到其定義時所在的物件。

ES5 code: (注意this綁定到bob object)

var bob = {
  _name: "Bob",
  _friends: ['aaa', 'bbb', 'ccc'],
  printFriends: function printFriends() {
    var _this = this;

    this._friends.forEach(function (f) {
      return console.log(_this._name + " knows " + f);
    });
  }
};

Classes

Classes就是 prototype-bases OO語法的糖衣, 支援prototype-based inheritance, super calls, instances和 static methods, constructors:

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

有C++, Python, Java或是其他OO的概念, 更方便上手了。

Enhanced Object Literals

Object literals被延伸用來在建立時期支援設定prototype:

var obj = {
    // __proto__
    __proto__: theProtoObj,
    // Shorthand forhandler: handlerhandler,
    // Methods
    toString() {
     // Super calls
     return "d " + super.toString();
    },
    // Computed (dynamic) property names
    [ 'prop_' + (() => 42)() ]: 42
};

Template Strings

Template strings提供一個建立string的語法糖。(很像Perl, Python)

多行字串, 注意是使用鍵盤左上角的back-tick符號, 而不是常見的單引號:

// Multiline strings
`In ES5 this is
 not legal.`

相當於ES5的:

"In ES5 this is\n not legal.";

Template string:

// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

相當於ES5的:

// Interpolate variable bindings
var name = "Bob", time = "today";
"Hello " + name + ", how are you " + time + "?";

Destructuring

var [a, ,b] = [1, 2, 3];
var {c,d} = {a:1, d:2};
var [e, f, ...g] = [1,2,3,4,5]; //ES7語法 babel也支援!
var [h = 1] = [];

console.log(a); // 1
console.log(b); // 2
console.log(c); // undefined
console.log(d); // 2
console.log(g); // [3,4,5]
console.log(h); // 1

有了Destructuring功能, 可以很方便的做到兩個變數值互換, 不再需要額外的temp變數了:

var a = 1;
var b = 3;
[a, b] = [b, a];

實現了 multi-values return功能, 從傳回的multi-values取我們想要的值:

function f(){
  return [1, 2, 3];
}
var a, b;
[a, b, c]=f(); //a為1, b為2, c為3
var c = f();//c為[1,2]
var [d, ,e] = f(); //d為1, e為3

Default + Rest + Spread

以前要寫一個帶有預設值參數的JS函式很搞剛, 例如我想寫個function, 有兩個參數x, y, 我像要y的預設參數值為12, 那在ES5會寫成這樣:

function f(x) {
  var y = arguments[1] === undefined ? 12 : arguments[1];
  return x + y;
}
f(3) == 15;

用ES6:

var f = (x, y=12) => x +y;

現在可以很方便的撰寫具有預設參數值的函式:

function drawES6Chart({
  size = 'big', 
  cords = { x: 0, y: 0 }, 
  radius = 25} = {}) 
{
  console.log(size, cords, radius);
}

//輸出: big { x: 18, y: 30 } 30
drawES6Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});

//輸出: big { x: 0, y: 0 } 25
drawES6Chart();

Rest:

var f= (x, ...y) => x * y.length;
f(3, "hello", true); //傳回6

Spread:

var f = (x, y, z) => x + y + z;
f(...[1,2,3]); //6

let + const

let用來宣告block scope的變數,

const就是 single-assignment, 避免重複賦值:

function f() {
  {
    let x;
    {
      // okay, block scoped name
      const x = "sneaky";
      // error, const不能重複賦值
      x = "foo";
    }
    // error, 在同一個block裏面不能重複宣告
    let x = "inner";
  }
}

varlet的不同, 可以由以下來比較:

function foo() {
  console.log( x ); //undefined
  console.log( y ); //error

  var x = 1;
  if (x === 1) {
    let y = 2;
  }
  console.log( y ); //error
}

foo();
console.log( x ); //error

第一個列印x得到的值為undefined, 因為變數x還沒有賦與值, 接下來列印兩個y會得到error, 因為我們用的是let宣告y,為block scope, y只存在if這個block裏面, 列印的y並沒有在這個block裏面, 因此會列印出錯誤。

最後一個列印x也會得到error, 因為我們用var宣告x, 最後一個列印x已經超出function foo()的scope之外。

let變成宣告為function scope也很簡單, 只要把let宣告放在function內容的第1行就好了! 例如:

function foo( x ) {
  let y;

  if (x === 1) {
    y = 2;
  }
  console.log( y );
}

foo( 1 ); //2
console.log( y ); //error

More

Overview of ES6 features

Arrow functions - MDN

Destructuring assignment - MDN

let - MDN

Es6 varaible declarations

<< 回到文章列表