ES6標準 Arrow Function(箭頭函式=>)

廖雪峰發表於2020-05-18

ES6標準新增了一種新的函式:Arrow Function(箭頭函式),為什麼叫Arrow Function?因為它的定義用的就是一個箭頭

ES6—箭頭函式

0.為什麼會出現箭頭函式?

1.傳統的javascript函式語法並沒有提供任何的靈活性,每一次你需要定義一個函式時,你都必須輸入function () {},這至少會出現兩個問題,ES6箭頭函式都圓滿解決了它,

第一個問題:程式碼輸入快了容易輸錯成 funciton或者functoin或者其它,但是=>這個玩意你要是再寫錯就只能說有點過分了。

第二個問題:節省大量程式碼,我們先不用管下面的ES6程式碼為什麼這樣的語法能實現同樣的功能,我們就直觀的感受一下程式碼量。

ES5寫法:

function addFive(num){
   return num+5; 
}
alert(addFive(10));

ES6寫法:

var addFive = num=>num+5;
alert(addFive(5));

沒有function、沒有return,沒有(),沒有{},這些全變成了浮雲,世界好清靜。

從上面我們就可以看到,使用箭頭函式不僅僅能夠避免錯誤,同時還能讓我們少一丟丟程式碼,當然實際工作中遠比這個程式碼量節省更多。
一方面是因為積累效應,每一部分少一丟丟合起來就多了,一方面是它還有更能節省程式碼和大幅提高工作效率的場景。

接下來我們就說說今天的主角--箭頭函式。

ES6標準新增了一種新的函式:Arrow Function(箭頭函式),也稱“胖箭頭函式”, 允許

使用“箭頭”(=>)定義函式,是一種簡寫的函式表示式。

1、箭頭函式語法

在ES5中我們實現一個求和的函式:

var sum = function(x, y) {
  return x + y
}

要使用箭頭函式,可以分兩步實現同樣的函式功能:

首先使用=>來替代關鍵詞function

var sum = (x, y) => {
  return x + y
}

這個特性非常好!!!

前面我們已經說過用=>來替代關鍵詞function就意味著不會寫錯function了,這真是一個絕妙的設計思想!

其次,函式體只有一條返回語句時, 我們可以省略括號{}和return關鍵詞:

var sum = (x, y) => x + y

再誇張一點點,如果只有一個引數時,()可省略。

這是箭頭函式最簡潔的形式,常用於作用簡單的處理函式,比如過濾:

// ES5
var array = ['1', '2345', '567', '89'];
array = array.filter(function (item) {
  return item.length > 2;
});
// ["2345", "567"]
 
 
// ES6
let array = ['1', '2345', '567', '89'];
array = array.filter(item => item.length > 2); 
// ["2345", "567"]

箭頭函式的主要使用模式如下:

// 一個引數對應一個表示式
param => expression;// 例如 x => x+2;
 
// 多個引數對應一個表示式
(param [, param]) => expression; //例如 (x,y) => (x + y);
 
// 一個引數對應多個表示式
param => {statements;} //例如 x = > { x++; return x;};
 
// 多個引數對應多個表示式
([param] [, param]) => {statements} // 例如 (x,y) => { x++;y++;return x*y;};
 
//表示式裡沒有引數
() => expression; //例如var flag = (() => 2)(); flag等於2
 
() => {statements;} //例如 var flag = (() => {return 1;})(); flag就等於1
 
 //傳入一個表示式,返回一個物件
([param]) => ({ key: value });
//例如 var fuc = (x) => ({key:x})
    var object = fuc(1);
    alert(object);//{key:1}

大家不要覺得好多啊,好麻煩,其實這些根本不復雜。投入一次,受益終生。(怎麼感覺我像賣保險的……),寫一兩次你就習慣新的寫法了。

2、箭頭函式中的 this

箭頭函式內的this值繼承自外圍作用域。執行時它會首先到它的父作用域找,如果父作用域還是箭頭函式,那麼接著向上找,直到找到我們要的this指向。

我們先看一道經典的關於this的面試題:

var name = 'leo';
var teacher = {
  name: "大彬哥",
  showName: function () {
    function showTest() {
      alert(this.name);
    }
    showTest();
  }
};
teacher.showName();//結果是 leo,而我們期待的是大彬哥,這裡this指向了window,我們期待指向teacher

大家知道,ES5中的this說好聽了叫"靈活",說不好聽就是瞎搞,特別容易出問題.而且面試還非常愛考,工作更不用說了,經常給我們開發搗亂,出現不好除錯的bug,用E箭頭函式解決這個問題就很得心應手了。

var name = 'leo';
var teacher = {
  name: "大彬哥",
  showName: function () {
    let showTest = ()=>alert(this.name);
    showTest();
  }
};
teacher.showName();

箭頭函式中的this其實是父級作用域中的this。箭頭函式引用了父級的變數詞法作用域就是一個變數的作用在定義的時候就已經被定義好,當在本作用域中找不到變數,就會一直向父作用域中查詢,直到找到為止。

由於this在箭頭函式中已經按照詞法作用域繫結了,所以,用call或者apply呼叫箭頭函式時,無法對this進行繫結,即傳入的第一個引數被忽略:

var obj = {
  birth: 1996,
  getAge: function (year) {
    var b = this.birth; // 1996
    var fn = (y) => y - this.birth; // this.birth仍是1996
    return fn.call({birth:1990}, year);
  }
};
obj.getAge(2018); // 22 ( 2018 - 1996)
由於this已經在詞法層面完成了繫結,透過call或apply方法呼叫一個函式時,只是傳入了引數而已,對this並沒有什麼影響 。因此,這個設計節省了開發者思考上下文繫結的時間。

3、箭頭函式的特性

3.1 箭頭函式沒有 arguments

箭頭函式不僅沒有this,常用的arguments也沒有。如果你能獲取到arguments,那它

一定是來自父作用域的。
function foo() {
 return () => console.log(arguments)
}
 
foo(1, 2)(3, 4) // 1,2

上例中如果箭頭函式有arguments,就應該輸出的是3,4而不是1,2。

箭頭函式不繫結arguments,取而代之用rest引數…解決

var foo = (...args) => {
 return args
}
 
console.log(foo(1,3,56,36,634,6))  // [1, 3, 56, 36, 634, 6]

箭頭函式要實現類似純函式的效果,必須剔除外部狀態。我們可以看出,箭頭函式除了傳入的引數之外,真的在普通函式里常見的this、arguments、caller是統統沒有的!

如果你在箭頭函式引用了this、arguments或者引數之外的變數,那它們一定不是箭頭函式本身包含的,而是從父級作用域繼承的。

3.2 箭頭函式中不能使用 new

  this.name = name;
};
let one = new Person("galler");

執行該程式,則出現TypeError: Person is not a constructor

3.3 箭頭函式可以與變數解構結合使用。

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

3.4 箭頭函式沒有原型屬性

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

由此可以看出箭頭函式沒有原型。

另一個錯誤是在原型上使用箭頭函式,如:

function A() {
 this.foo = 1
}
 
A.prototype.bar = () => console.log(this.foo)
 
let a = new A()
a.bar() //undefined

同樣,箭頭函式中的this不是指向A,而是根據變數查詢規則回溯到了全域性作用域。同樣,使用普通函式就不存在問題。箭頭函式中不可加new,也就是說箭頭函式不能當建構函式進行使用。

3.5 箭頭函式不能換行

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

如果開發中確實一行搞不定,邏輯很多,就加{},你就想怎麼換行怎麼換行了。

var func = ()=>{
  return '來啊!互相傷害啊!'; // 1.加{} 2.加return
}

4、箭頭函式使用場景

JavaScript中this的故事已經是非常古老了,每一個函式都有自己的上下文。

以下例子的目的是使用jQuery來展示一個每秒都會更新的時鐘:

$('.time').each(function () {
 setInterval(function () {
  $(this).text(Date.now());
 }, 1000);
});

當嘗試在setInterval的回撥中使用this來引用DOM元素時,很不幸,我們得到的只是一個屬於回撥函式自身

上下文的this。一個通常的解決辦法是定義一個that或者self變數:

$('.time').each(function () {
 var self = this;
 setInterval(function () {
  $(self).text(Date.now());
 }, 1000);
});

但當使用箭頭函式時,這個問題就不復存在了。因為它不產生屬於它自己上下文的this:

$('.time').each(function () {
 setInterval(() => $(this).text(Date.now()), 1000);
});

箭頭函式的另一個用處是簡化回撥函式。

// 正常函式寫法
[1,2,3].map(function (x) {
 return x * x;
});
 
// 箭頭函式寫法
[1,2,3].map(x => x * x);

當然也可以在事件監聽函式里使用:

document.body.addEventListener('click', event=>console.log(event, this));
// EventObject, BodyElement

5、總結

5.1 箭頭函式優點

箭頭函式是使用=>語法的函式簡寫形式。這在語法上與 C#、Java 8 、Python( lambda 函式)和 CoffeeScript 的

相關特性非常相似。

非常簡潔的語法,使用箭頭函式比普通函式少些動詞,如:function或return。

() => { ... } // 零個引數用 () 表示。
x => { ... } // 一個引數可以省略 ()。
(x, y) => { ... } // 多引數不能省略 ()。

如果只有一個return,{}可以省略。
更直觀的作用域和 this的繫結,它能讓我們能很好的處理this的指向問題。箭頭函式加上let關鍵字的使用,將會讓我們javascript程式碼上一個層次。

5.2 箭頭函式使用場景

箭頭函式適合於無複雜邏輯或者無副作用的純函式場景下,例如用在map、reduce、filter的回撥函式定義
中,另外目前vue、react、node等庫,都大量使用箭頭函式,直接定義function的情況已經很少了。
各位同學在寫新專案的時候,要不斷的琢磨箭頭函式使用場景、特點,享受使用箭頭函式帶來的便利,這樣才能更快地成長。

下面是廖雪峰網站的補充

ES6標準新增了一種新的函式:Arrow Function(箭頭函式)。

為什麼叫Arrow Function?因為它的定義用的就是一個箭頭:

x => x * x

上面的箭頭函式相當於:

function (x) {
return x * x;
}

箭頭函式相當於匿名函式,並且簡化了函式定義。箭頭函式有兩種格式,一種像上面的,只包含一個表示式,連{ ... }和return都省略掉了。還有一種可以包含多條語句,這時候就不能省略{ ... }和return:

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

如果引數不是一個,就需要用括號()括起來:

// 兩個引數:
(x, y) => x * x + y * y
 
// 無引數:
() => 3.14
 
// 可變引數:
(x, y, ...rest) => {
 var i, sum = x + y;
 for (i=0; i<rest.length; i++) {
  sum += rest[i];
 }
 return sum;
}

如果要返回一個物件,就要注意,如果是單表示式,這麼寫的話會報錯:

// SyntaxError:
x => { foo: x }

因為和函式體的{ ... }有語法衝突,所以要改為:

// ok:
x => ({ foo: x })

this

箭頭函式看上去是匿名函式的一種簡寫,但實際上,箭頭函式和匿名函式有個明顯的區別:箭頭函式內部的this是詞法作用域,由上下文確定。

回顧前面的例子,由於JavaScript函式對this繫結的錯誤處理,下面的例子無法得到預期結果:

var obj = {
 birth: 1990,
 getAge: function () {
  var b = this.birth; // 1990
  var fn = function () {
   return new Date().getFullYear() - this.birth; // this指向window或undefined
  };
  return fn();
 }
};

現在,箭頭函式完全修復了this的指向,this總是指向詞法作用域,也就是外層呼叫者obj:

var obj = {
 birth: 1990,
 getAge: function () {
  var b = this.birth; // 1990
  var fn = () => new Date().getFullYear() - this.birth; // this指向obj物件
  return fn();
 }
};
obj.getAge(); // 25

如果使用箭頭函式,以前的那種hack寫法:

var that = this;

就不再需要了。

由於this在箭頭函式中已經按照詞法作用域繫結了,所以,用call()或者apply()呼叫箭頭函式時,無法對this進行繫結,即傳入的第一個引數被忽略:

var obj = {
 birth: 1990,
 getAge: function (year) {
  var b = this.birth; // 1990
  var fn = (y) => y - this.birth; // this.birth仍是1990
  return fn.call({birth:2000}, year);
 }
};
obj.getAge(2015); // 25

練習

請使用箭頭函式簡化排序時傳入的函式:

<script>
'use strict'
var arr = [10, 20, 1, 2];
arr.sort((x, y) => {
 if (x < y) return -1
 else if (x > y) return 1
 else return 0
});
console.log(arr); // [1, 2, 10, 20]
</script>


以上就是ES6標準 Arrow Function(箭頭函式=>)的詳細內容

相關文章