1. 程式人生 > 其它 >es6中的=>箭頭函式

es6中的=>箭頭函式

js中es6 =>箭頭函式的使用總結

箭頭函式是在es6中新增的新特性,主要是為了簡化函式的寫法,相當於一種匿名函式的表達,並且箭頭函式不繫結this

x => x * x 相當於 function(x){return x*x}

1、箭頭函式的格式

箭頭函式有兩種格式, 一種只包含一個表示式,沒有{…} 和 return 。 一種包含多條語句, 這個時候{} return 就不能省略

x => {
     if (x>0){
         return x*x
     }else{
        return x
     }
}

如果有多個引數就要用():

注意這裡rest用法的使用,rest[i],可以取到其中引數

// 兩個引數返回後面的值
(x, y) =>x*y + y*y
//沒有引數
() => 999
// 可變引數
(x, y, ...rest) =>{
    var i,sum = x+y;
    for (i=0;i<rest.length;i++){
        sum += rest[i];
    }
    return sum;
}
//加括號的函式體返回物件字面量表達式:
params => ({foo: bar})
//同樣支援引數列表解構
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6
var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

elements.map(function(element) {
  return element.length;
}); // 返回陣列:[8, 6, 7, 9]

// 上面的普通函式可以改寫成如下的箭頭函式
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式只有一個引數時,可以省略引數的圓括號
elements.map(element => {
 return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式的函式體只有一個 `return` 語句時,可以省略 `return` 關鍵字和方法體的花括號
elements.map(element => element.length); // [8, 6, 7, 9]

// 在這個例子中,因為我們只需要 `length` 屬性,所以可以使用引數解構
// 需要注意的是字串 `"length"` 是我們想要獲得的屬性的名稱,而 `lengthFooBArX` 則只是個變數名,
// 可以替換成任意合法的變數名
elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]

2、沒有單獨的this

在箭頭函數出現之前,每一個新函式根據它是被如何呼叫的來定義這個函式的this值:

  • 如果該函式是一個建構函式,this指標指向一個新的物件
  • 在嚴格模式下的函式呼叫下,this指向undefined
  • 如果該函式是一個物件的方法,則它的this指標指向這個物件
  • 等等

This被證明是令人厭煩的面向物件風格的程式設計。

function Person() {
  // Person() 建構函式定義 `this`作為它自己的例項.
  this.age = 0;

  setInterval(function growUp() {
    // 在非嚴格模式, growUp()函式定義 `this`作為全域性物件,
    // 與在 Person()建構函式中定義的 `this`並不相同.
    this.age++;
  }, 1000);
}

var p = new Person();

Copy to Clipboard

在ECMAScript 3/5中,通過將this值分配給封閉的變數,可以解決this問題。

function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // 回撥引用的是`that`變數, 其值是預期的物件.
    that.age++;
  }, 1000);
}

Copy to Clipboard

或者,可以建立繫結函式,以便將預先分配的this值傳遞到繫結的目標函式(上述示例中的growUp()函式)。

箭頭函式不會建立自己的this,它只會從自己的作用域鏈的上一層繼承this。因此,在下面的程式碼中,傳遞給setInterval的函式內的this與封閉函式中的this值相同:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 正確地指向 p 例項
  }, 1000);
}

var p = new Person();

Copy to Clipboard

與嚴格模式的關係

鑑於 this 是詞法層面上的,嚴格模式中與 this 相關的規則都將被忽略。

var f = () => { 'use strict'; return this; };
f() === window; // 或者 global

嚴格模式的其他規則依然不變.

通過 call 或 apply 呼叫

由於 箭頭函式沒有自己的this指標,通過 call() apply() 方法呼叫一個函式時,只能傳遞引數(不能繫結this---譯者注),他們的第一個引數會被忽略。(這種現象對於bind方法同樣成立---譯者注)

var adder = {
  base : 1,

  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };

    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 輸出 2
console.log(adder.addThruCall(1)); // 仍然輸出

不繫結arguments

箭頭函式不繫結Arguments 物件。因此,在本示例中,arguments只是引用了封閉作用域內的arguments:

var arguments = [1, 2, 3];
var arr = () => arguments[0];

arr(); // 1

function foo(n) {
  var f = () => arguments[0] + n; // 隱式繫結 foo 函式的 arguments 物件. arguments[0] 是 n,即傳給foo函式的第一個引數
  return f();
}

foo(1); // 2
foo(2); // 4
foo(3); // 6
foo(3,2);//6

Copy to Clipboard

在大多數情況下,使用剩餘引數是相較使用arguments物件的更好選擇。

function foo(arg) {
  var f = (...args) => args[0];
  return f(arg);
}
foo(1); // 1

function foo(arg1,arg2) {
    var f = (...args) => args[1];
    return f(arg1,arg2);
}
foo(1,2);  //2

Copy to Clipboard

使用箭頭函式作為方法

如上所述,箭頭函式表示式對非方法函式是最合適的。讓我們看看當我們試著把它們作為方法時發生了什麼。

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b();
// undefined, Window{...}
obj.c();
// 10, Object {...}

Copy to Clipboard

箭頭函式沒有定義this繫結。另一個涉及Object.defineProperty()的示例:

'use strict';
var obj = {
  a: 10
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this);
    return this.a+10;
   // 代表全域性物件 'Window', 因此 'this.a' 返回 'undefined'
  }
});

obj.b; // undefined   "undefined"   Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}

Copy to Clipboard

使用 new 操作符

箭頭函式不能用作構造器,和 new一起用會丟擲錯誤。

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

Copy to Clipboard

使用prototype屬性

箭頭函式沒有prototype屬性。

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

Copy to Clipboard

使用 yield 關鍵字

yield 關鍵字通常不能在箭頭函式中使用(除非是巢狀在允許使用的函式內)。因此,箭頭函式不能用作函式生成器。

函式體

箭頭函式可以有一個“簡寫體”或常見的“塊體”。

在一個簡寫體中,只需要一個表示式,並附加一個隱式的返回值。在塊體中,必須使用明確的return語句。

var func = x => x * x;
// 簡寫函式 省略return

var func = (x, y) => { return x + y; };
//常規編寫 明確的返回值

Copy to Clipboard

返回物件字面量

記住用params => {object:literal}這種簡單的語法返回物件字面量是行不通的。

var func = () => { foo: 1 };
// Calling func() returns undefined!

var func = () => { foo: function() {} };
// SyntaxError: function statement requires a name

Copy to Clipboard

這是因為花括號({} )裡面的程式碼被解析為一系列語句(即 foo 被認為是一個標籤,而非物件字面量的組成部分)。

所以,記得用圓括號把物件字面量包起來:

var func = () => ({foo: 1});

Copy to Clipboard

換行

箭頭函式在引數和箭頭之間不能換行。

var func = ()
           => 1;
// SyntaxError: expected expression, got '=>'

Copy to Clipboard

但是,可以通過在 ‘=>’ 之後換行,或者用 ‘( )’、'{ }'來實現換行,如下:

var func = (a, b, c) =>
  1;

var func = (a, b, c) => (
  1
);

var func = (a, b, c) => {
  return 1
};

var func = (
  a,
  b,
  c
) => 1;

// 不會有語法錯誤

Copy to Clipboard

解析順序

雖然箭頭函式中的箭頭不是運算子,但箭頭函式具有與常規函式不同的特殊運算子優先順序解析規則。

let callback;

callback = callback || function() {}; // ok

callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments

callback = callback || (() => {});    // ok

Copy to Clipboard

更多示例

// 空的箭頭函式返回 undefined
let empty = () => {};

(() => 'foobar')();
// Returns "foobar"
// (這是一個立即執行函式表示式,可參閱 'IIFE'術語表)


var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];

var sum = arr.reduce((a, b) => a + b);
// 66

var even = arr.filter(v => v % 2 == 0);
// [6, 0, 18]

var double = arr.map(v => v * 2);
// [10, 12, 26, 0, 2, 36, 46]

// 更簡明的promise鏈
promise.then(a => {
  // ...
}).then(b => {
  // ...
});

// 無引數箭頭函式在視覺上容易分析
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    // deeper code
    console.log('I happen later');
  }, 1);
}, 1);

Copy to Clipboard

箭頭函式也可以使用條件(三元)運算子:

var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

Copy to Clipboard

箭頭函式內定義的變數及其作用域

// 常規寫法
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

Copy to Clipboard

箭頭函式也可以使用閉包:

// 標準的閉包函式
function A(){
      var i=0;
      return function b(){
              return (++i);
      };
};

var v=A();
v();    //1
v();    //2


//箭頭函式體的閉包( i=0 是預設引數)
var Add = (i=0) => {return (() => (++i) )};
var v = Add();
v();           //1
v();           //2

//因為僅有一個返回,return 及括號()也可以省略
var Add = (i=0)=> ()=> (++i);

Copy to Clipboard

箭頭函式遞迴

var fact = (x) => ( x==0 ?  1 : x*fact(x-1) );
fact(5);       // 120

Copy to Clipboard

規範

||參考MDN箭頭函式

箭頭函式 - JavaScript | MDN (mozilla.org)