1.1函式引數的預設值
1.1.1用法
ES6允許為函式引數設定預設值,直接寫在引數定義的後面。
// ES5
function log(x, y){
x = x || 'hello';
console.log(x,' ',y);
}
log(false,'world'); // hello world
//ES6
function log(x = 'hello', y){
console.log(x,' ',y);
}
log(false,'world'); // false world
複製程式碼
ES6寫法的好處:簡潔、程式碼可讀性高、利於程式碼優化
注意:引數變數是預設宣告,不能用let或const重複宣告;不能有同名引數;引數預設值不傳值,每次重新計算預設值表示式的值。
// 惰性求值
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
複製程式碼
1.1.2結合解構賦值使用
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
// 如果函式引數不預設為空物件,foo()會報錯
複製程式碼
1.1.3引數預設值位置
注意引數預設值的位置,最好寫在最後面,容易看出來。如果非尾部引數設定預設值,這個引數實際上無法省略。
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
複製程式碼
1.1.4函式length屬性
length屬性的含義:該函式預期傳入的引數個數。某個引數指定預設值以後,預期傳入的引數不包括這個引數。
所以指定預設值後,length屬性會失真。如果設定預設值的引數不是尾引數,length屬性不計後面的引數。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
複製程式碼
1.1.5作用域
一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。
let x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
複製程式碼
1.2rest引數
rest引數形式為...變數名(陣列),用來獲取多於引數,可以替代arguments物件,寫法更簡潔
// arguments變數的寫法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest引數的寫法
const sortNumbers = (...numbers) => numbers.sort();
複製程式碼
1.3name屬性
函式的name屬性,返回該函式的函式名。如果將一個匿名函式賦值給一個變數,ES6會返回實際的函式名,ES5返回空字串。其他情況看程式碼。
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
(new Function).name // "anonymous"
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
複製程式碼
箭頭函式(重點)
用法
ES6允許使用箭頭定義函式,如果箭頭函式不需要引數或需要多個引數,使用圓括號代表引數部分,如果只有一個引數,可以省略圓括號;如果程式碼塊多於一條語句,用大括號括起來,並使用return語句返回,如果只有程式碼塊只有一條語句,可以省略大括號和return。
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;
};
複製程式碼
由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號,否則會報錯。
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
複製程式碼
箭頭函式與變數解構結合
let person = {
first: 'hello',
last: 'world',
}
const full = ({first,last}) => first + ' ' + last;
console.log(full(person)); // hello world
複製程式碼
簡化回撥函式
// 正常函式寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函式寫法
[1,2,3].map(x => x * x);
複製程式碼
可以與rest引數結合
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
複製程式碼
注意點
- 函式體內的this物件就是定義時所在的物件,而不是使用時所在的物件。箭頭函式的this物件不會發生改變。
- 不可以當作建構函式。箭頭函式沒有constructor方法,所以不可以當作建構函式,也不可以使用new命令。
- 不支援arguments物件,該物件在函式體內不存在,可以用rest引數來實現arguments物件的效果。
- 不可以使用yield命名,不能用作generator函式。
// this指向問題
function foo1(){
setTimeout(()=>{
console.log('箭頭函式id:',this.id);
},1000);
}
function foo2(){
setTimeout(function(){
console.log('非箭頭函式id',this.id);
},1000);
}
var id = 21;
foo1.call({ id: 42 }); // 42
foo2.call({ id: 42 }); // 21
複製程式碼
不適用場合
- 定義物件的方法,該方法內部包括this
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
cat.jumps();
console.log(cat.lives); // 9
複製程式碼
並沒有達到預期效果
- 需要動態this的時候
const button = document.querySelector('button');
button.addEventListener('click', () => {
this.textContent = 'Loading...';
});
// this 並不是指向預期的 button 元素,而是 window
複製程式碼
- 函式體很複雜,或者內部有大量讀寫,不單純為了計算值,要用普通函式,可以提高程式碼可讀性。
巢狀的箭頭函式
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
複製程式碼
摘自:阮一峰