使用Babel處理ES6的 this 和 Arrow Function(箭頭函式)

2018/09/05 posted in  javascript comments

在Javascript ES6標準中,我們可以用Arrow function來幫助我們解決bind的問題,但由於user的browser不一定support ES6,我們需要用Babel來向下兼容。

以下會詳述有關.bind()、Arrow function和Babel的原理。

為甚麼要使用arrow function

之前一篇文章,我們談到this在各種情況下會指不同的object。在ES6的class裡,經常需要用到this來指定class自身,但這並不是件容易的事,因為this會取決於執行時的環境而定。

一般我們需要在constructor裡bind this來確保得到正確的this:

class Example {
    constructor() {
        this.something = "Hello World";
        this.printSomething = this.printSomething.bind(this);
    }
    printSomething() {
        console.log(this.something);
    }
}

當你有十個function,就需要bind十次。很麻煩是吧。

但使用了ES6的arrow function,class fucntion裡面的this必然會是class自身:

class Example {
    something = "Hello World"
    printSomething = () => {
        console.log(this.something);
    }
}

向下兼容問題

Arrow function是ES6的其中一個feature,而ES6算是比較新的語法,所以未必所有瀏覽器都支援新語法。

為了讓更多使用者能用到我們的Application,我們可以用Babel轉譯ES6語法成為ES5甚至以前的語法。

Babel transform

Babel是一個JS編譯器,主要用途就是把一些用新語法(例如ES6)寫成的JS編譯成兼容的舊語法,讓未更新的browser都可以執行。

以下是Babel對arrow function所做的轉譯。

ES6

class Test {
  world = 1
  hello = () => {
    console.log(this.world);
  }
}

轉譯後

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Test = function Test() {
  var _this = this;

  _classCallCheck(this, Test);

  this.world = 1;

  this.hello = function () {
    console.log(_this.world);
  };
};

大家也可以到官方online compiler自己試一下。

我們可以看出主要有幾個重點:

  1. Class會變成function
  2. 這個function會define所有class properties(就像constructor一樣做法)
  3. Arrow function裡的this會變成local的_this來確保正確reference

知道多一點Babel的背後運作,用起來就更得心應手了。

Appendix: .bind(this)

以下附上bind this的例子:

ES6

class Example {
    constructor() {
        this.something = "Hello World";
        this.printSomething = this.printSomething.bind(this);
    }
    printSomething() {
        console.log(this.something);
    }
}

轉譯後

"use strict";

var _createClass = function () {...}();

function _classCallCheck(instance, Constructor) { ... }

var Example = function () {
    function Example() {
        _classCallCheck(this, Example);

        this.something = "Hello World";
        this.printSomething = this.printSomething.bind(this);
    }

    _createClass(Example, [{
        key: "printSomething",
        value: function printSomething() {
            console.log(this.something);
        }
    }]);

    return Example;
}();