Javascript 做爲一等函數。是一個高級開發者應該掌握的。爲何會被稱做一等?不只僅是它能夠被當作變量同樣處理。下面有三個關鍵點表明了 Javascript 一等函數的特色:javascript
針對以上特色,咱們來作幾個真實的複雜的例子。java
咱們建立一個返回文本 「Hello」 的函數,而後把該函數賦值給變量 sayHello
。ios
const sayHello = () => {
return 'Hello';
};
console.log(sayHello());
// "Hello"
複製代碼
這裏有個區別是關於函數聲明和函數表達式的區別。這個 StackOverflow 問題已經將其解釋的清清楚楚。推薦有英文基礎的同窗瞭解一下。其實答案也很簡單,他們之間的區別在於變量提高(hoisting)。具體到複雜的生成環境中,這個狀況仍是須要多多注意的,加上代碼結構的影響,什麼時候使用函數聲明,什麼時候使用表達式,是一個須要多多思考的問題。好比:在 if 表達式中,使用函數聲明是無效的,會被提高到 if 以外,而使用 let 相關的表達式則能夠限定該函數的做用範圍。面試
使用以前提到的 sayHello
方法,把該方法當作參數傳遞給 sayHelloToPerson
。在函數 sayHelloToPerson
中,變量 greeter
將會指向 sayHello
方法的內存地址。當咱們調用 greeter()
時,sayHello
就被調用了。axios
const sayHelloToPerson = (greeter, person) => {
return greeter() + ' ' + person;
};
console.log(sayHelloToPerson(sayHello, 'John'));
// Hello John
複製代碼
單純的說這個做爲參數傳遞函數有點簡單了。咱們再舉一個你們必定用過的例子:api
let array = [1,2,3,4,5,6];
array.map(item => item * 2);
複製代碼
當你想對一個數組進行一些格式化或者改寫的時候, map
是最容易想到的方法。map 裏面其實就是接受了一個匿名函數,這個函數就是被當作參數傳入的,只是匿名而已。同時,map 的使用方法也是一種高階函數的表現。怎麼樣,是否是以爲一會兒高大上了許多。關於數組的方法,最值得研究的 reduce,這裏不展開討論,不過能夠出個題目供你們在留言區嘗試解決一下:如何使用reduce實現一個map?數組
若是咱們不想輸出 Hello
,而是想自定義建立任何輸出的形式。那麼咱們須要一個能夠建立輸出語的函數。閉包
const greeterMaker = greeting => {
return person => {
return greeting + ' ' + person;
};
};
const sayHelloToPerson = greeterMaker('Hello');
const sayHowdyToPerson = greeterMaker('Howdy');
console.log(sayHelloToPerson('Joanne'));
// "Hello Joanne"
console.log(sayHowdyToPerson('Joanne'));
// "Howdy Joanne"
複製代碼
上圖中的 greeterMaker
接受了一個參數而且把該參數做爲返回的一個匿名函數的參數。經過傳遞的參數來定義問候語。咱們在使用 jQuery
時,經常看到的 $(this).find().eq(0)
這樣鏈式調用的寫法,其關鍵在於每一個方法都返回了當前的 this 對象。這個思路與上面返回函數也有必定的類似之處。函數
假設有一組由對象構成的用戶信息數據,須要咱們校驗。咱們能夠經過校驗函數來判斷,這些數據是否合法。oop
const usernameLongEnough = obj => {
return obj.username.length >= 5;
};
const passwordsMatch = obj => {
return obj.password === obj.confirmPassword;
};
const objectIsValid = (obj, ...funcs) => {
for (let i = 0; i < funcs.length; i++) {
if (funcs[i](obj) === false) {
return false;
}
}
return true;
};
const obj1 = {
username: 'abc123',
password: 'foobar',
confirmPassword: 'foobar',
};
const obj1Valid = objectIsValid(obj1, usernameLongEnough, passwordsMatch);
console.log(obj1Valid);
// true
const obj2 = {
username: 'joe555',
password: 'foobar',
confirmPassword: 'oops',
};
const obj2Valid = objectIsValid(obj2, usernameLongEnough, passwordsMatch);
console.log(obj2Valid);
// false
複製代碼
假設須要使用 API key 來調用 API。咱們須要給每個請求添加這個 key。那麼咱們能夠建立一個方法把這個 API key 做爲參數保存在一個方法中,而後返回這個方法。
const apiConnect = apiKey => {
const getData = route => {
return axios.get(`${route}?key=${apiKey}`);
};
const postData = (route, params) => {
return axios.post(route, {
body: JSON.stringify(params),
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
};
return { getData, postData };
};
const api = apiConnect('my-secret-key');
// No need to include the apiKey anymore
api.getData('http://www.example.com/get-endpoint');
api.postData('http://www.example.com/post-endpoint', { name: 'Joe' });
複製代碼
Javascript 中的函數還承擔着實現類的功能。儘管 Javascript 中沒有真正的類,它是基於原型繼承的方式來實現封裝、繼承和多態的特性。關於這裏的只是,在【Javascript高級程序設計】一書中有詳細講解。函數是一類特殊的對象。咱們來看看下面的例子來解釋這個緣由:
console.log(Function instanceof Object);
// true
console.log(Object instanceof Function);
// true
複製代碼
函數是一類特殊的對象,那麼 Function
做爲 Object
的一個實例是正常的。不只是 Function
,String
,Array
,Number
等,都是 Object
的實例。然而 Object instanceof Function
該作如何解釋? 實際上,全部的構造函數,本質上仍是函數。一個函數必然是 Function
的實例啦!上面這個問題曾經是我遇到的面試題。可是沒有答對。仍是理解不深入。在這裏總結反思一下,查漏補缺。