重讀《深入理解ES6》—— 函式

李等等扣丁發表於2019-03-06

在 ES6 之前,JavaScript 的函式語法幾乎沒有發生太大的變化,而遺留的一些問題導致實現一些基本的功能經常要編寫很多程式碼。而 ES6 對函式的大力度改進,讓我們編寫函式時更加方便且更少出錯。

而關於引數的處理以及箭頭函式是我非常喜歡的功能。

一、函式形參的預設值

JavaScript 中的函式有一個比較特別的地方是形參和實參的數量可以不相同。當定義的形參沒有預設值傳入的時候,JavaScript 會為我們自動傳入一個預設值。

在 ES5 的階段,我們通常會自己設定一個預設值,就像下面這樣:

function getAge(age) {
    age = age || 10;
    console.log(age);
}

getAge(12); // 12
getAge(0); // 10
複製程式碼

這段程式碼想告訴我們,當傳入引數時,我們就取傳入的值,如果沒有傳入,我們就取預設的 10。但是這通常會有一個問題,當我們傳入一個 0 的時候,它也會被預設為一個假值,並最終將 age 賦值為 10。

在這種情況下,我們往往通過 typeof 檢查引數型別,來實現相關功能:

function getAge(age) {
    age = (typeof age !== "undefined") ? age : 10;
    console.log(age);
}

getAge(12); // 12
getAge(0); // 10
複製程式碼

這種寫法可以幫助我們更好地處理引數,但仔細想想,若是有多個引數時,這會讓我們寫許多額外的程式碼。幸好 ES6 引數預設值設定的出現,讓我們可以很好的解決這個問題。

function getAge(age=10) {
    console.log(age);
}
getAge(); // 10,使用預設值
getAge(12); // 12,不使用預設值
getAge(0); // 0,不適用預設值,且傳入引數為假值的情況
複製程式碼

值得注意的一點是:

function getAge(age=10, newAge=age) {
	console.log(newAge);
}
getAge(); // 10
複製程式碼

你可以把第二個引數的預設值設定為第一個引數,但是不能把第一個引數的預設值設定為第二個引數。

另一方面,函式的預設值還可以是一個表示式:

function getValue() {
    return 3;
}

function add(first, second=getValue()) {
    return first + second;
}

add(1, 3); // 4
add(2); // 5
複製程式碼

二、箭頭函式

箭頭函式是一個比較有趣的特性。因為簡單的語法,大家工作中也經常用到,所以大家也比較熟悉,下面就簡單說說箭頭函式的語法。

箭頭函式有1個引數,且函式體只有一條語句

let f = value => value;

// 相當於

let f = function(value) {
    return value;
};
複製程式碼

箭頭函式沒有引數或者有多個引數,引數部分需要使用小括號

let f = () => 20;
// 相當於 
let f = function() {
    return 20
};

let f = (a, b) => a + b;
// 相當於
let f = function(a, b) {
    return a + b;
}
複製程式碼

箭頭函式的函式體內有多條語句

let f = (a, b) => {
    return a * b;
};
複製程式碼

想讓箭頭函式返回一個物件字面量,需要將該字面量包裹在小括號裡

let f = () => ({ id: 1, name: 'zhangsan', age: 18 });

// 相當於

let f = function() {
    return {
        id: 1,
        name: 'zhangsan',
        age: 18
    };
}
複製程式碼

簡單的介紹完箭頭函式的基本語法,我們來看看箭頭函式的一些主要特性:

1、箭頭函式沒有 this 繫結

箭頭函式中沒有 this 繫結,必須通過查詢作用域鏈來決定其值。如果箭頭函式被非箭頭函式包含,則 this 繫結的是最近一層非箭頭函式的 this ;否則,this 的值會被設定為全域性物件。

或者再簡單一點來說:箭頭函式的 this 指向離它最近的父級作用域

舉個例子:

// 普通函式
let person = {
    name: 'zhangsan',
    age: 18,
    say: function() {
        console.log(this); // person 物件
        console.log(this.name); // 'zhangsan'
        console.log(this.age);  // 18
    }
};

person.say(); // 普通函式,誰呼叫,this 指向誰
複製程式碼
// 箭頭函式
let person = {
    name: 'zhangsan',
    age: 18,
    say: () => {
        console.log(this); // window 物件
        console.log(this.name); // undefined
        console.log(this.age);  // undefined
    }
};

person.say(); // 箭頭函式,沒有 this,函式內部的 this,指向父級作用域,即全域性
複製程式碼

最後,因為箭頭函式中沒有 this ,所以也不能用 call()、apply()、bind() 等方法改變 this 的指向。

2、箭頭函式沒有原型,不能通過 new 來呼叫

箭頭函式的設計初衷是”即用即棄“,所以不能用它來定義新型別。所以,箭頭函式中沒有 prototype 屬性,自然不能例項化,也就是不能用 new 關鍵字來呼叫。

let Person = () => {};
let p1 = new Person(); // Uncaught TypeError: Person is not a constructor

let Person = () => {};
console.log(Person.prototype); // undefined
複製程式碼
3、沒有 argments 繫結
// 普通函式
let person = function() {
    console.log(arguments);
}
person(1); // 列印 arguments 物件
複製程式碼
// 箭頭函式
let person = () => {
    console.log(arguments); 
}

person(1); // Uncaught ReferenceError: arguments is not defined
複製程式碼

總的來說,箭頭函式和一些小特性的加入,可以使我們的編碼更加方便。

友情連結

重讀《深入理解ES6》 —— 塊級作用域

重讀《深入理解ES6》 —— 模板字串

如果文章中錯誤或表述不嚴謹的地方,歡迎指正

也歡迎大家關注我的同名微信公眾號:李等等扣丁

重讀《深入理解ES6》—— 函式

相關文章