箭頭函數你想知道的都在這裏

一、基本語法回顧

咱們先來回顧下箭頭函數的基本語法。
ES6 增長了箭頭函數:html

var f = v => v;

// 等同於
var f = function (v) {
  return v;
};

若是箭頭函數不須要參數或須要多個參數,就使用一個圓括號表明參數部分。react

var f = () => 5;
// 等同於
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同於
var sum = function(num1, num2) {
  return num1 + num2;
};

因爲大括號被解釋爲代碼塊,因此若是箭頭函數直接返回一個對象,必須在對象外面加上括號,不然會報錯。git

// 報錯
let getTempItem = id => { id: id, name: "Temp" };

// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });

下面是一種特殊狀況,雖然能夠運行,但會獲得錯誤的結果。github

let foo = () => { a: 1 };
foo() // undefined

上面代碼中,原始意圖是返回一個對象{ a: 1 },可是因爲引擎認爲大括號是代碼塊,因此執行了一行語句a: 1。這時,a能夠被解釋爲語句的標籤,所以實際執行的語句是1;,而後函數就結束了,沒有返回值。ajax

關於做用域json

箭頭函數內定義的變量及其做用域

// 常規寫法
var greeting = () => {let now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined 標準的let做用域

// 參數括號內定義的變量是局部變量(默認參數)
var greeting = (now=new Date()) => "Good" + (now.getHours() > 17 ? " evening." : " day.");
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined

// 對比:函數體內{}不使用var定義的變量是全局變量
var greeting = () => {now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();           //"Good day."
console.log(now);     // Fri Dec 22 2017 10:01:00 GMT+0800 (中國標準時間)

// 對比:函數體內{} 用var定義的變量是局部變量
var greeting = () => {var now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now);    // ReferenceError: now is not defined

二、關於this

2.一、默認綁定外層this

箭頭函數沒有 this,因此須要經過查找做用域鏈來肯定 this 的值。
這就意味着若是箭頭函數被非箭頭函數包含,this 綁定的就是最近一層非箭頭函數的 this。app

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

上面代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到 100 毫秒後。若是是普通函數,執行時this應該指向全局對象window,這時應該輸出21。可是,箭頭函數致使this老是指向函數定義生效時所在的對象(本例是{id: 42}),因此輸出的是42
箭頭函數可讓setTimeout裏面的this,綁定定義時所在的做用域,而不是指向運行時所在的做用域。函數

因此,箭頭函數轉成 ES5 的代碼以下。性能

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

2.二、 不能用call()、apply()、bind()方法修改裏面的this

(function() {
  return [
    (() => this.x).bind({ x: 'inner' })() // 無效的bind,最終this仍是指向外層
  ];
}).call({ x: 'outer' });
// ['outer']

上面代碼中,箭頭函數沒有本身的this,因此bind方法無效,內部的this指向外部的this學習

三、沒有 arguments

箭頭函數沒有本身的 arguments 對象,這不必定是件壞事,由於箭頭函數能夠訪問外圍函數的 arguments 對象:

function constant() {
    return () => arguments[0]
}

var result = constant(1);
console.log(result()); // 1

那若是咱們就是要訪問箭頭函數的參數呢?

你能夠經過命名參數或者 rest 參數的形式訪問參數:

let nums = (...nums) => nums;

四、 不能經過 new 關鍵字調用

JavaScript 函數有兩個內部方法:[[Call]][[Construct]]

當經過new調用函數時,執行[Construct]]方法,建立一個實例對象,而後再執行函數體,將 this 綁定到實例上。

當直接調用的時候,執行[[Call]]方法,直接執行函數體。

箭頭函數並無[[Construct]]方法,不能被用做構造函數,若是經過 new 的方式調用,會報錯。

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor

五、沒有原型

因爲不能使用new調用箭頭函數,因此也沒有構建原型的需求,因而箭頭函數也不存在prototype這個屬性。

var Foo = () => {};
console.log(Foo.prototype); // undefined

五、不適用場合

第一個場合是定義函數的方法,且該方法內部包括this。

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

上面代碼中,cat.jumps()方法是一個箭頭函數,這是錯誤的。調用cat.jumps()時,若是是普通函數,該方法內部的this指向cat;若是寫成上面那樣的箭頭函數,使得this指向全局對象,所以不會獲得預期結果。
第二個場合是須要動態this的時候,也不該使用箭頭函數。

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

上面代碼運行時,點擊按鈕會報錯,由於button的監聽函數是一個箭頭函數,致使裏面的this就是全局對象。若是改爲普通函數,this就會動態指向被點擊的按鈕對象。

六、使用場景

下面這個是咱們開發常常遇到的。咱們通常會經過this賦值給一個變量,而後再經過變量訪問。

class Test {
    constructor() {
        this.birth = 10;  
    }
    submit(){
        let self = this;
        $.ajax({          
            type: "POST",
            dataType: "json",
            url: "xxxxx" ,//url
            data: "xxxxx",
            success: function (result) {
                console.log(self.birth);//10
            },
            error : function() {}                   
        });
    }
}
let test = new Test();
test.submit();//undefined

這裏咱們就能夠經過箭頭函數來解決

...
success: (result)=> {
    console.log(this.birth);//10
},
...

箭頭函數在react中的運用場景

class Foo extends Component {
  constructor(props) {
    super(props);
  }
  handleClick() {
    console.log('Click happened', this);
    this.setState({a: 1});
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

在react中咱們這樣直接調用方法是有問題的,在handleClick函數中的this是有問題,咱們平時須要這麼作

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened', this);
    this.setState({a: 1});
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

這裏經過this.handleClick.bind(this)給函數綁定this。可是這樣寫起來有些麻煩,有沒有簡單的方法呢?這時候咱們的箭頭函數就出場了

class Foo extends Component {
  // Note: this syntax is experimental and not standardized yet.
  handleClick = () => {
    console.log('Click happened', this);
    this.setState({a: 1});
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

箭頭函數中 this 的值是繼承自 外圍做用域,很好的解決了這個問題。
除此以外咱們還能夠用箭頭函數傳參(這個不是必須的),並且會有性能問題。更多信息請查看

const A = 65 // ASCII character code

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i)).
    };
  }
  handleClick(letter) {
    this.setState({ justClicked: letter });
  }
  render() {
    return (
      <div>
        Just clicked: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} onClick={() => this.handleClick(letter)}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

最後
更多系列文章請看

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star對做者也是一種鼓勵。
相關文章
相關標籤/搜索