箭頭函數(=>)

ES6 允許使用“箭頭”(=>)定義函數。

一.箭頭函數

1.基本定義

  • ① 簡單寫法,一個變量一個表達式返回單值

    var f = v => v;
    
    // 等同於
    var f = function (v) {
      return v;
    };
    
  • ② 空參數

    var f = () => 5;
    // 等同於
    var f = function () { return 5 };
    
  • ③ 多參數

    	var sum = (num1, num2) => num1 + num2;
    // 等同於
    var sum = function(num1, num2) {
      return num1 + num2;
    };
    
  • ④ 函數體中多條語句,使用return語句返回並使用大括號將函數體包圍起來。

    var sum = (num1, num2) => { 
    	num1+=1;
    	return num1 + num2; }
    
  • ⑤ 函數結構體也可以不返回

    //一 單行不返回使用void關鍵字標識
    let fn = () => void doesNotReturn();
    // 二 多行語句不使用return
    var sum = (num1, num2) => { 
    num1+=1;
    num1 + num2; }
    
  • ⑥ 如果箭頭函數直接返回一個對象,由於大括號被解釋爲代碼塊,必須在對象外面加上括號,否則會報錯。

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

2. 箭頭函數與變量解構結合使用

  • 箭頭函數可以與變量解構結合使用。

    	const full = ({ first, last }) => first + ' ' + last;
    
    // 等同於
    function full(person) {
      return person.first + ' ' + person.last;
    }
    

3. 箭頭函數的使用範例

  • 箭頭函數使得表達更加簡潔。

    const isEven = n => n % 2 === 0;
    const square = n => n * n;
    
  • 簡化回調函數

    // 正常函數寫法
    [1,2,3].map(function (x) {
      return x * x;
    });
    
    // 箭頭函數寫法
    [1,2,3].map(x => x * x);
    
    // 正常函數寫法
    var result = values.sort(function (a, b) {
      return a - b;
    });
    
    // 箭頭函數寫法
    var result = values.sort((a, b) => a - b);
    
  • rest 參數與箭頭函數結合的例子

    const numbers = (...nums) => nums;
    
    numbers(1, 2, 3, 4, 5)
    // [1,2,3,4,5]
    
    const headAndTail = (head, ...tail) => [head, tail];
    
    headAndTail(1, 2, 3, 4, 5)
    // [1,[2,3,4,5]]
    

二.箭頭函數使用注意點

1.對this指向的影響

  • this對象表示當前對象運行的上下文環境,普通函數有自己的作用域,其定義的this指向就是本身的運行環境,若在對象中運行此函數就是指向此對象,若在全局中運行就指向全局對象;而對象不形成作用域,若對象屬性直接定義this或者使用箭頭函數定義(非普通函數),則指向上級運行環境(大多是頂級對象window)

     const cat = {
    	thisDefine: this,//object window
    	lives: 9,
    	jumps: () => {
    	  this.lives--;
    	  alert(this.lives)//NaN
    	},
    	jumps2: function () {
    	  this.lives--;
    	  alert(this.lives)//8
    	}
      }
      alert(cat.thisDefine)//object window
      cat.jumps()
      alert(cat.lives)//9
      cat.jumps2()
      alert(cat.lives)//8
    
  • 箭頭函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

    //一 call方法使對象具有調用foo和foo2函數的能力
    //二 setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到 100 毫秒後。如果是普通函數,執行時this應該指向全局對象window,這時應該輸出21。但是,箭頭函數導致this總是指向函數定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。
    //三 箭頭函數可以讓setTimeout裏面的this,綁定定義時所在的作用域,而不是指向運行時所在的作用域。
      var id = 21;
      function foo() {
    	setTimeout(() => {
    	  console.log('id:', this.id);
    	}, 100);
      }
    
      function foo2() {
    	setTimeout(function(){
    	  console.log('id:', this.id);
    	}, 100);
      }
      foo2.call({ id: 49 });
      //id: 21
      foo.call({ id: 42 });
      // id: 42
    

2.箭頭函數不可以當作構造函數

  • 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。

3.箭頭函數沒有自己的this,指向外層對象的this

  • 箭頭函數中this實質:this指向的固定化,並不是因爲箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因爲它沒有this,所以也就不能用作構造函數。

    var handler = {
      id: '123456',
    
      init: function() {
    	document.addEventListener('click',
    	  event => this.doSomething(event.type), false);
      },
    
      doSomething: function(type) {
    	console.log('Handling ' + type  + ' for ' + this.id);
      }
    };
    
  • 範例

    //下面代碼之中,只有一個this,就是函數foo的this,所以t1、t2、t3都輸出同樣的結果。因爲所有的內層函數都是箭頭函數,都沒有自己的this,它們的this其實都是最外層foo函數的this。
    function foo() {
      return () => {
    	return () => {
    	  return () => {
    		console.log('id:', this.id);
    	  };
    	};
      };
    }
    
    var f = foo.call({id: 1});
    
    var t1 = f.call({id: 2})()(); // id: 1
    var t2 = f().call({id: 3})(); // id: 1
    var t3 = f()().call({id: 4}); // id: 1
    

4.箭頭函數除了this,以下三個變量在箭頭函數之中也是不存在的,指向外層函數的對應變量:argumentssupernew.target

  • 箭頭函數內部的變量arguments,其實是函數foo的arguments變量。

    function foo() {
      setTimeout(() => {
    	console.log('args:', arguments);
      }, 100);
    }
    
    foo(2, 4, 6, 8)
    // args: [2, 4, 6, 8]
    
  • 由於箭頭函數沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。

    //箭頭函數沒有自己的this,所以bind方法無效,內部的this指向外部的this。
    (function() {
      return [
    	(() => this.x).bind({ x: 'inner' })()
      ];
    }).call({ x: 'outer' });
    // ['outer']
    

三.不適宜使用箭頭函數場景

1.由於箭頭函數使得this從“動態”變成“靜態”,實質是內部沒有this指向,拷貝上級對象this指向

  • 第一個場合是定義對象的方法,且該方法內部包括this

    //因爲對象不構成單獨的作用域,導致jumps箭頭函數定義時的作用域就是全局作用域this指向全局對象。jumps2定義在普通函數中,形成了作用域,this指向cat對象
      const cat = {
    	lives: 9,
    	jumps: () => {
    	  this.lives--;
    	  alert(this.lives)//NaN
    	},
    	jumps2: function () {
    	  this.lives--;
    	  alert(this.lives)//8
    	}
      }
      cat.jumps()
      alert(cat.lives)//9
      cat.jumps2()
      alert(cat.lives)//8
    
  • 第二個場合是需要動態this的時候,也不應使用箭頭函數。

    //下面代碼運行時,點擊按鈕會報錯,因爲button的監聽函數是一個箭頭函數,導致裏面的this就是全局對象。如果改成普通函數,this就會動態指向被點擊的按鈕對象。
    var button = document.getElementById('press');
    button.addEventListener('click', () => {
      this.classList.toggle('on');
    });
    
發佈了181 篇原創文章 · 獲贊 65 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章