ES6:函數(shù)

標(biāo)簽: rest spread 箭頭函數(shù) JavaScript ES6 前端 web
本博客版權(quán)歸本人和饑人谷所有,轉(zhuǎn)載需說明來源
內(nèi)容轉(zhuǎn)載自阮一峰老師的ES6入門

1.rest參數(shù)

ES6 引入 rest 參數(shù)(形式為“...變量名”),用于獲取函數(shù)的多余參數(shù)骡尽,這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組昭娩,該變量將多余的參數(shù)放入數(shù)組中咧欣。

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

上面代碼的add函數(shù)是一個求和函數(shù)臭墨,利用 rest 參數(shù)藏鹊,可以向該函數(shù)傳入任意數(shù)目的參數(shù)润讥。

下面是一個 rest 參數(shù)代替arguments變量的例子。

// arguments變量的寫法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest參數(shù)的寫法
const sortNumbers = (...numbers) => numbers.sort();

上面代碼的兩種寫法伙判,比較后可以發(fā)現(xiàn)象对,rest 參數(shù)的寫法更自然也更簡潔。

rest 參數(shù)中的變量代表一個數(shù)組宴抚,所以數(shù)組特有的方法都可以用于這個變量。下面是一個利用 rest 參數(shù)改寫數(shù)組push方法的例子甫煞。

function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, 1, 2, 3)

注意菇曲,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個參數(shù)),否則會報錯抚吠。

// 報錯
function f(a, ...b, c) {
  // ...
}

函數(shù)的length屬性常潮,不包括 rest 參數(shù)。

(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

2.擴(kuò)展運算符

含義

擴(kuò)展運算符(spread)是三個點(...)楷力。它好比 rest 參數(shù)的逆運算喊式,將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列。

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]

該運算符主要用于函數(shù)調(diào)用萧朝。

function push(array, ...items) {
  array.push(...items);
}

function add(x, y) {
  return x + y;
}

var numbers = [4, 38];
add(...numbers) // 42

上面代碼中岔留,array.push(...items)和add(...numbers)這兩行,都是函數(shù)的調(diào)用检柬,它們的都使用了擴(kuò)展運算符献联。該運算符將一個數(shù)組,變?yōu)閰?shù)序列。

擴(kuò)展運算符與正常的函數(shù)參數(shù)可以結(jié)合使用里逆,非常靈活进胯。

function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);

替代數(shù)組apply方法

由于擴(kuò)展運算符可以展開數(shù)組,所以不再需要apply方法原押,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了胁镐。

// ES5的寫法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的寫法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f(...args);

下面是擴(kuò)展運算符取代apply方法的一個實際的例子,應(yīng)用Math.max方法诸衔,簡化求出一個數(shù)組最大元素的寫法盯漂。

// ES5的寫法
Math.max.apply(null, [14, 3, 77])

// ES6的寫法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

上面代碼表示,由于JavaScript不提供求數(shù)組最大元素的函數(shù)署隘,所以只能套用Math.max函數(shù)采幌,將數(shù)組轉(zhuǎn)為一個參數(shù)序列芳誓,然后求最大值。有了擴(kuò)展運算符以后,就可以直接用Math.max了雨膨。

另一個例子是通過push函數(shù),將一個數(shù)組添加到另一個數(shù)組的尾部幢泼。

// ES5的寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6的寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

上面代碼的ES5寫法中渡嚣,push方法的參數(shù)不能是數(shù)組,所以只好通過apply方法變通使用push方法脾还。有了擴(kuò)展運算符伴箩,就可以直接將數(shù)組傳入push方法。

下面是另外一個例子鄙漏。

// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);    

3.箭頭函數(shù)

基本用法

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

var f = v => v;

上面的箭頭函數(shù)等同于:

var f = function(v) {
  return v;
};

如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分怔蚌。

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

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

如果箭頭函數(shù)的代碼塊部分多于一條語句巩步,就要使用大括號將它們括起來,并且使用return語句返回桦踊。

var sum = (num1, num2) => { return num1 + num2; }

由于大括號被解釋為代碼塊椅野,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號籍胯。

var getTempItem = id => ({ id: id, name: "Temp" });

箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用竟闪。

const full = ({ first, last }) => first + ' ' + last;

// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}

箭頭函數(shù)使得表達(dá)更加簡潔。

const isEven = n => n % 2 == 0;
const square = n => n * n;

上面代碼只用了兩行杖狼,就定義了兩個簡單的工具函數(shù)炼蛤。如果不用箭頭函數(shù),可能就要占用多行本刽,而且還不如現(xiàn)在這樣寫醒目鲸湃。

箭頭函數(shù)的一個用處是簡化回調(diào)函數(shù)赠涮。

// 正常函數(shù)寫法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);

另一個例子是

// 正常函數(shù)寫法
var result = values.sort(function (a, b) {
  return a - b;
});

// 箭頭函數(shù)寫法
var result = values.sort((a, b) => a - b);

下面是rest參數(shù)與箭頭函數(shù)結(jié)合的例子。

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]]

使用注意點

箭頭函數(shù)有幾個使用注意點暗挑。

(1)函數(shù)體內(nèi)的this對象笋除,就是定義時所在的對象,而不是使用時所在的對象炸裆。

(2)不可以當(dāng)作構(gòu)造函數(shù)垃它,也就是說,不可以使用new命令烹看,否則會拋出一個錯誤国拇。

(3)不可以使用arguments對象,該對象在函數(shù)體內(nèi)不存在惯殊。如果要用酱吝,可以用Rest參數(shù)代替。

(4)不可以使用yield命令土思,因此箭頭函數(shù)不能用作Generator函數(shù)务热。

上面四點中,第一點尤其值得注意己儒。this對象的指向是可變的崎岂,但是在箭頭函數(shù)中,它是固定的闪湾。

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

var id = 21;

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

上面代碼中冲甘,setTimeout的參數(shù)是一個箭頭函數(shù),這個箭頭函數(shù)的定義生效是在foo函數(shù)生成時途样,而它的真正執(zhí)行要等到100毫秒后江醇。如果是普通函數(shù),執(zhí)行時this應(yīng)該指向全局對象window何暇,這時應(yīng)該輸出21嫁审。但是,箭頭函數(shù)導(dǎo)致this總是指向函數(shù)定義生效時所在的對象(本例是{id: 42})赖晶,所以輸出的是42。

箭頭函數(shù)可以讓setTimeout里面的this辐烂,綁定定義時所在的作用域遏插,而不是指向運行時所在的作用域。下面是另一個例子纠修。

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭頭函數(shù)
  setInterval(() => this.s1++, 1000);
  // 普通函數(shù)
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代碼中胳嘲,Timer函數(shù)內(nèi)部設(shè)置了兩個定時器,分別使用了箭頭函數(shù)和普通函數(shù)扣草。前者的this綁定定義時所在的作用域(即Timer函數(shù))颜屠,后者的this指向運行時所在的作用域(即全局對象)。所以鹰祸,3100毫秒之后甫窟,timer.s1被更新了3次,而timer.s2一次都沒更新蛙婴。

箭頭函數(shù)可以讓this指向固定化粗井,這種特性很有利于封裝回調(diào)函數(shù)。下面是一個例子街图,DOM事件的回調(diào)函數(shù)封裝在一個對象里面浇衬。

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);
  }
};

上面代碼的init方法中,使用了箭頭函數(shù)餐济,這導(dǎo)致這個箭頭函數(shù)里面的this耘擂,總是指向handler對象。否則絮姆,回調(diào)函數(shù)運行時醉冤,this.doSomething這一行會報錯,因為此時this指向document對象滚朵。

this指向的固定化冤灾,并不是因為箭頭函數(shù)內(nèi)部有綁定this的機制,實際原因是箭頭函數(shù)根本沒有自己的this辕近,導(dǎo)致內(nèi)部的this就是外層代碼塊的this韵吨。正是因為它沒有this,所以也就不能用作構(gòu)造函數(shù)移宅。

所以归粉,箭頭函數(shù)轉(zhuǎn)成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);
}

上面代碼中漏峰,轉(zhuǎn)換后的ES5版本清楚地說明了糠悼,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this浅乔。

請問下面的代碼之中有幾個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

上面代碼之中,只有一個this靖苇,就是函數(shù)foo的this席噩,所以t1、t2贤壁、t3都輸出同樣的結(jié)果悼枢。因為所有的內(nèi)層函數(shù)都是箭頭函數(shù),都沒有自己的this脾拆,它們的this其實都是最外層foo函數(shù)的this馒索。

除了this莹妒,以下三個變量在箭頭函數(shù)之中也是不存在的,指向外層函數(shù)的對應(yīng)變量:arguments绰上、super旨怠、new.target。

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

foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]

上面代碼中渔期,箭頭函數(shù)內(nèi)部的變量arguments运吓,其實是函數(shù)foo的arguments變量。

另外疯趟,由于箭頭函數(shù)沒有自己的this拘哨,所以當(dāng)然也就不能用call()、apply()信峻、bind()這些方法去改變this的指向倦青。

(function() {
  return [
    (() => this.x).bind({ x: 'inner' })()
  ];
}).call({ x: 'outer' });
// ['outer']

上面代碼中,箭頭函數(shù)沒有自己的this盹舞,所以bind方法無效产镐,內(nèi)部的this指向外部的this。

長期以來踢步,JavaScript語言的this對象一直是一個令人頭痛的問題癣亚,在對象方法中使用this,必須非常小心获印。箭頭函數(shù)”綁定”this述雾,很大程度上解決了這個困擾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兼丰,一起剝皮案震驚了整個濱河市玻孟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鳍征,老刑警劉巖黍翎,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異艳丛,居然都是意外死亡匣掸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門氮双,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旺聚,“玉大人,你說我怎么就攤上這事眶蕉。” “怎么了唧躲?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵造挽,是天一觀的道長碱璃。 經(jīng)常有香客問我,道長饭入,這世上最難降的妖魔是什么嵌器? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谐丢,結(jié)果婚禮上爽航,老公的妹妹穿的比我還像新娘。我一直安慰自己乾忱,他們只是感情好讥珍,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窄瘟,像睡著了一般衷佃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹄葱,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天氏义,我揣著相機與錄音,去河邊找鬼图云。 笑死惯悠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竣况。 我是一名探鬼主播克婶,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼帕翻!你這毒婦竟也來了鸠补?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嘀掸,失蹤者是張志新(化名)和其女友劉穎紫岩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睬塌,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡泉蝌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揩晴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勋陪。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖硫兰,靈堂內(nèi)的尸體忽然破棺而出诅愚,到底是詐尸還是另有隱情,我是刑警寧澤劫映,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布违孝,位于F島的核電站刹前,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雌桑。R本人自食惡果不足惜喇喉,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望校坑。 院中可真熱鬧拣技,春花似錦、人聲如沸耍目。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽制妄。三九已至掸绞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耕捞,已是汗流浹背衔掸。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俺抽,地道東北人敞映。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像磷斧,于是被迫代替她去往敵國和親振愿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 函數(shù)參數(shù)的默認(rèn)值 基本用法 在ES6之前弛饭,不能直接為函數(shù)的參數(shù)指定默認(rèn)值冕末,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,392評論 0 1
  • 1.函數(shù)參數(shù)的默認(rèn)值 (1).基本用法 在ES6之前侣颂,不能直接為函數(shù)的參數(shù)指定默認(rèn)值档桃,只能采用變通的方法。
    趙然228閱讀 690評論 0 0
  • ES6函數(shù)的擴(kuò)展 1.函數(shù)默認(rèn)值 定義:ES6允許為函數(shù)設(shè)定默認(rèn)值憔晒,即直接寫在參數(shù)定義的后面 示例function...
    lijaha閱讀 407評論 0 0
  • 三藻肄,字符串?dāng)U展 3.1 Unicode表示法 ES6 做出了改進(jìn),只要將碼點放入大括號拒担,就能正確解讀該字符嘹屯。有了這...
    eastbaby閱讀 1,537評論 0 8
  • 姓名:徐群杰 公司:寧波大發(fā) 【日精進(jìn)打卡第20天】 【知~學(xué)習(xí)】 《六項精進(jìn)》大綱1遍 ...
    徐群杰閱讀 183評論 0 0