若是你正在學習JavaScript,必定會遇到高階函數這個術語。 雖然聽起來很複雜,但事實並不是如此。 JavaScript 適用於函數式編程的緣由是它適合使用高階函數。 高階函數在JavaScript中普遍使用。 若是你已經用 JavaScript 編程了一段時間,你可能已經使用過它們只是不知道。要徹底理解這個概念,首先必須瞭解函數式編程是什麼以及第一類函數的概念。編程
在大多數簡單的術語中,函數編程是一種編程形式,你能夠將函數做爲參數傳遞給其餘函數,並將它們做爲值返回。在函數式編程中,咱們在函數中思考和編碼。 JavaScript,Haskell,Clojure,Scala和Erlang是實現函數式編程的一些語言。數組
若是你一直在學習JavaScript,可能據說過 JavaScript 將函數視爲一等公民。 那是由於在 JavaScript 或任何其餘函數式編程語言中,函數是對象。 在JavaScript中,函數是一種特殊類型的對象。它們是Function對象。 例如:編程語言
function greeting() {
console.log('Hello World');
}
// 函數調用
greeting(); // 打印 'Hello World'
複製代碼
爲了證實函數是JavaScript中的對象,咱們能夠這樣作:函數式編程
// 像處理對象同樣給函數添加屬性
greeting.lang = 'English';
// 打印 'English'
console.log(greeting.lang);
複製代碼
注意 - 雖然這在JavaScript中徹底有效,但這被認爲是一種有害的作法。 不該該向函數對象添加隨機屬性,若是必須,請使用對象。 在JavaScript中,可使用其餘類型(如對象,字符串或數字)執行全部操做,可使用函數。 能夠將它們做爲參數傳遞給其餘函數(回調),將它們分配給變量並傳遞它們等等。這就是JavaScript中的函數被稱爲First-Class函數的緣由。函數
在JavaScript中,我在JavaScript中們能夠把函數賦值給變量。例如:學習
const square = function(x) {
return x * x;
}
// prints 25
square(5);
複製代碼
咱們也能夠傳遞它們。 例如:測試
const foo = square;
// prints 36
foo(6);
複製代碼
咱們能夠將函數做爲參數傳遞給其餘函數。 例如:ui
function formalGreeting() {
console.log("How are you?");
}
function casualGreeting() {
console.log("What's up?");
}
function greet(type, greetFormal, greetCasual) {
if(type === 'formal') {
greetFormal();
} else if(type === 'casual') {
greetCasual();
}
}
// 打印 'What's up?'
greet('casual', formalGreeting, casualGreeting);
複製代碼
如今咱們知道了第一類函數是什麼,接下來讓咱們深刻了解JavaScript中的高階函數。編碼
高階函數是對其餘函數進行操做的函數,能夠將它們做爲參數或經過返回它們。簡單來講,高階函數是一個函數,它接收函數做爲參數或將函數做爲輸出返回。 例如,Array.prototype.map,Array.prototype.filter和Array.prototype.reduce是語言中內置的一些高階函數。spa
讓咱們看一下內置高階函數的一些例子,看看它與咱們不使用高階函數的解決方案比較。
map()方法經過調用做爲輸入數組中每一個元素的參數提供的回調函數來建立一個新數組。 map()方法將從回調函數中獲取每一個返回的值,並使用這些值建立一個新數組。 傳遞給map()方法的回調函數接受3個參數:element,index和array。 咱們來看一些例子:
假設咱們有一個數組,咱們想要建立一個新數組,其中包含第一個數組的每一個值的兩倍。讓咱們看看如何使用和不使用高階函數來解決問題。
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);
複製代碼
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
return item * 2;
});
console.log(arr2);
複製代碼
咱們可使用箭頭函數語法使其更短:
const arr1 = [1, 2, 3];
const arr2 = arr1.map(item => item * 2);
console.log(arr2);
複製代碼
假設咱們有一個包含不一樣人的出生年份的數組,咱們想要建立一個包含其年齡的數組。 例如:
const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = [];
for(let i = 0; i < birthYear.length; i++) {
let age = 2018 - birthYear[i];
ages.push(age);
}
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);
複製代碼
const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = birthYear.map(year => 2018 - year);
// prints [ 43, 21, 16, 23, 33 ]
console.log(ages);
複製代碼
filter()方法建立一個新數組,其中包含經過回調函數提供的測試的全部元素。 傳遞給filter()方法的回調函數接受3個參數:element,index和array。 咱們來看一些例子:
假設咱們有一個包含名稱和年齡屬性的對象的數組。 咱們想要建立一個僅包含完全年齡(年齡大於或等於18)的人的數組。
const persons = [
{ name: 'Peter', age: 16 },
{ name: 'Mark', age: 18 },
{ name: 'John', age: 27 },
{ name: 'Jane', age: 14 },
{ name: 'Tony', age: 24},
];
const fullAge = [];
for(let i = 0; i < persons.length; i++) {
if(persons[i].age >= 18) {
fullAge.push(persons[i]);
}
}
console.log(fullAge);
複製代碼
const persons = [
{ name: 'Peter', age: 16 },
{ name: 'Mark', age: 18 },
{ name: 'John', age: 27 },
{ name: 'Jane', age: 14 },
{ name: 'Tony', age: 24},
];
const fullAge = persons.filter(person => person.age >= 18);
console.log(fullAge);
複製代碼
reduce方法對調用數組的每一個成員執行回調函數,從而產生單個輸出值。 reduce方法接受兩個參數:1)reducer函數(回調),2)和一個可選的initialValue。 reducer函數(回調)接受四個參數:accumulator,currentValue,currentIndex,sourceArray。 若是提供了initialValue,則累加器將等於initialValue,currentValue將等於數組中的第一個元素。 若是沒有提供initialValue,則累加器將等於數組中的第一個元素,currentValue將等於數組中的第二個元素。
假設咱們必須總結一組數字:
const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
return accumulator + currentValue;
});
// prints 25
console.log(sum);
複製代碼
每次在數組中的每一個值上調用reducer函數時,累加器都會保留reducer函數返回的先前操做的結果,並將currentValue設置爲數組的當前值。 最後,結果存儲在sum變量中。 咱們還能夠爲此函數提供初始值:
const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce(function(accumulator, currentValue) {
return accumulator + currentValue;
}, 10);
// prints 35
console.log(sum);
複製代碼
const arr = [5, 7, 1, 8, 4];
let sum = 0;
for(let i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
// prints 25
console.log(sum);
複製代碼
您能夠看到使用高階函數使咱們的代碼更清晰,更簡潔,更簡潔。
到目前爲止,咱們看到了語言中內置的各類高階函數。 如今讓咱們建立本身的高階函數。 咱們假設JavaScript沒有原生地圖方法。 咱們能夠本身構建它,從而建立咱們本身的高階函數。 假設咱們有一個字符串數組,咱們但願將此數組轉換爲整數數組,其中每一個元素表示原始數組中字符串的長度。
const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C'];
function mapForEach(arr, fn) {
const newArray = [];
for(let i = 0; i < arr.length; i++) {
newArray.push(
fn(arr[i])
);
}
return newArray;
}
const lenArray = mapForEach(strArray, function(item) {
return item.length;
});
// prints [ 10, 6, 3, 4, 1 ]
console.log(lenArray);
複製代碼
在上面的例子中,咱們建立了一個高階函數mapForEach,它接受一個數組和一個回調函數fn。此函數循環遍歷提供的數組,並在每次迭代時調用newArray.push函數調用內的回調函數fn。 回調函數fn接收數組的當前元素並返回該元素的長度,該元素存儲在newArray中。 for循環完成後,返回newArray並將其分配給lenArray。
咱們已經瞭解了高階函數和一些內置的高階函數。咱們還學習瞭如何建立本身的高階函數。 簡而言之,高階函數是一個函數,能夠接收函數做爲參數,甚至能夠返回一個函數。高階函數就像常規函數同樣,具備接收和返回其餘函數的附加能力,即參數和輸出。