說明:本文參考阮一峰的ECMAScript 6 入門
概要
- 函數參數預設值(直接指派、解構預設指派);
- rest參數:(…參數名)将函數多餘參數放進一個數組,可使用數組特有方法;
- 嚴格模式:隻要函數參數使用了預設值、解構指派、rest參數就不能在在函數内部顯示指定嚴格模式;
- 箭頭函數: => ,箭頭函數沒有自己的this,其内部this指向永遠都是其定義時所在的對象。兩種不适用場合:定義對象的方法,該方法内部包含this;需要使用動态this,例如事件監聽的回調函數如果使用了箭頭函數,則該箭頭函數内部this會指向全局對象window,而不會指向監聽對象。
1.函數參數的預設值
function add(x = 1, y = 2 ){
return x + y
}
add() //3
與結構指派預設值結合使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
上面代碼隻使用了對象的解構指派預設值,沒有使用函數參數的預設值。隻有當函數foo的參數是一個對象時,變量x和y才會通過解構指派生成。如果函數foo調用時沒提供參數,變量x和y就不會生成,進而報錯。通過提供函數參數的預設值,就可以避免這種情況。
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
2.rest參數
ES6引入了rest參數(形式為…變量名),用于擷取函數的多餘參數,這樣就不需要使用arguments對象了。rest參數搭配的變量是一個數組,該變量将多餘的參數放進數組中。
function add(...values) {
let sum = 0
for (val of values) {
sum += val
}
return sum
}
add(2, 3, 5) //10
上面代碼,是一個求和函數,利用rest參數,可以向函數傳入任意數目的參數。
下面是一個rest參數代替arguments對象的一個例子。
//arguments變量的寫法
fucntion sortNumbers() {
return Array.prototype.slice.call(arguments).sort()
}
//rest參數的寫法
const sortNumbers = (...numbers) => numbers.sort()
上面代碼的兩種寫法,很明顯rest參數的寫法更簡潔些。
arguments對象,不是一個真正的數組,它是一個類似數組的對象,不具有數組特有的方法。是以需要使用call()方法将其轉化為數組,再使用數組的方法。而rest參數是一個真正的數組,數組特有的方法都可以使用。
注意,rest參數隻能是最後一個參數,否則會報錯。
//報錯 SyntaxError: Rest parameter must be last formal parameter
function f(a, ...b, c) {
//...
}
函數的length屬性,不包括rest參數。
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
3.嚴格模式
從ES5開始,函數内部可以設定為嚴格模式。
function foo (a, b) {
'use strict'
//code
}
ES6對此做了修改,隻要函數參數使用了預設值、解構指派或者擴充運算符,就不能顯示指定嚴格模式,否則會報錯。
//報錯
function d(a = 1){
'use strict'
}
//報錯
function e({a, b}) {
'use strict'
}
//報錯
function e(...a) {
'use strict'
}
這樣規定的原因是,函數内部指定嚴格模式,同時适用于函數參數和函數體。但函數參數先于函數體執行,這就會導緻函數參數不符合嚴格模式下時,隻會在進入函數體内部,發現需要使用嚴格模式,才會報錯。
兩種方法可以規避這種限制。
第一種是設定全局的嚴格模式。
'use strict'
function doSomething(a, b = a) {
// code
}
第二種是放在立即執行函數裡。
const doSomething = (function () {
'use strict'
return function (value = 2) {
return value
}
}())
4.箭頭函數
ES6 允許使用“箭頭”(=>)定義函數。
var f = v => v
//等同于
var f = function (v) {
return v
}
如果箭頭函數不需要參數或需要多個參數,就使用一個圓括号()代表參數部分。
var f = () => 5
//等同于
var f = function () {
return 5
}
var sum = (a, b) => a + b
//等同于
var sum = function (a, b) {
return a + b
}
如果箭頭函數的代碼塊部分多于一條語句,那麼需要使用大括号它們括起來,并使用return語句傳回。
由于大括号被解釋為代碼塊,當函數傳回一個對象時,就需要使用圓括号将對象括起來,否則會報錯。
//報錯
let item = id => {id: id, name: "temp"}
//不報錯
let item = id => ({id: id, name: "temp"})
如果箭頭函數隻有一條語句,且沒有傳回值,可以采用下面的寫法,就不用寫大括号了。
箭頭函數可以與變量解構結合使用。
var sum = ({ x, y }) => x + y
下面是rest參數和箭頭函數結合使用。
const numbers = (...nums) => nums
numbers(1, 2, 3, 4, 5) // [1, 2, 3, 4, 5]
使用注意點
箭頭函數有幾個使用注意點。
- 函數體内的this對象,不是使用時所在的對象,而是函數定義時所在的對象。
- 不可以當做構造函數,使用new指令會報錯。
- 不可以使用arguments對象,該對象在箭頭函數裡不存在。如果要使用,可以用rest參數代替。
- 不可以使用yield指令,是以箭頭函數不能用作 Generator 函數。
上面四點中,尤其第一點需要注意。this對象的指向是可變的,但是在箭頭函數中,它的指向是固定的。
function foo() {
setTimeout(() => {
console.log(this.id)
}, 100)
}
let id = 21
foo.call({id: 42})
上面代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效在foo函數生成時,而它的真正執行要等到 100 毫秒後。如果是普通函數,執行時this指向應該是window,這時應該輸出21。但是,箭頭函數this指向函數定義生效時所在的對象(本例是{id: 42}),是以輸出的是42。
this指向的固定化,并不是箭頭函數内部有綁定this的機制,而是因為箭頭函數根本沒有自己的this,導緻内部的this就是外層代碼塊的this。正是因為它沒有自己的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);
}
另外由于箭頭函數沒有自己的this,是以也就不能使用call()、apply()、bind()這些方法改變this的指向。
(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']
上面代碼中,箭頭函數沒有自己的this,是以bind方法無效,内部的this指向外部的this。
不适用場合
由于箭頭函數的this指向是固定的,下面兩種場合就不适合使用箭頭函數。
第一個場合是定義對象的方法,且該方法内部包含this。
const cat = {
lives: 9,
jumps: () => {
this.lives--
}
}
上面代碼中,cat.jumps()方法是一個箭頭函數,這是錯誤的。調用cat.jumps()時,如果是普通函數,該方法内部的this指向cat;如果寫成上面那樣的箭頭函數,使得this指向全局對象,是以不會得到預期結果。這是因為對象不構成單獨的作用域,導緻jumps箭頭函數定義時的作用域就是全局作用域。
第二個場合是需要動态this的時候,也不應使用箭頭函數。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面代碼運作時,點選按鈕會報錯,因為button的監聽函數是一個箭頭函數,導緻裡面的this就是全局對象。如果改成普通函數,this就會動态指向被點選的按鈕對象。