DailyENJS 致力於翻譯優秀的前端英文技術文章,爲技術同窗帶來更好的技術視野。javascript
若是你正在學習JavaScript,那麼你確定遇到太高階函數這個術語。雖然聽起來很複雜,但事實並不是如此。前端
使JavaScript適合函數式編程的緣由是它擁有高階函數。java
高階函數在JavaScript中被普遍使用。若是你已經使用了 JavaScript 一段時間,你可能不知不覺已經使用了它們。要徹底理解這個概念,首先必須瞭解函數式編程是什麼以及 First-Class Functions 的概念。編程
在大多數簡單的術語中,函數式編程是一種編程形式,你能夠將函數做爲參數傳遞給其餘函數,而且也能夠將它們做爲值返回。在函數式編程中,咱們以函數思惟進行思考和寫代碼。數組
JavaScript,Haskell,Clojure,Scala和Erlang都是實現函數式編程的語言。編程語言
若是你一直在學習JavaScript,你可能據說過JavaScript將函數視爲一等公民。那是由於在JavaScript或任何其餘函數式編程語言中,函數都是對象。函數式編程
在JavaScript中,函數是一種特殊類型的對象。它們是Function對象。例如:函數
function greeting() {
console.log('Hello World');
}
// Invoking the function
greeting(); // prints 'Hello World'
複製代碼
爲了證實函數是JavaScript中的對象,咱們能夠這麼作:學習
// We can add properties to functions like we do with objects
greeting.lang = 'English';
// Prints 'English'
console.log(greeting.lang)
複製代碼
注意: 雖然這在JavaScript中徹底OK,但這被認爲是一種有害的作法。你不該該向函數對象添加隨機屬性,若是必須添加屬性,請使用對象。ui
在JavaScript中,你可使用使用函數就像使用其餘類型(如對象,字符串或數字)同樣。你能夠將它們做爲參數傳遞給其餘函數(回調),將它們分配給變量並傳遞它們等等。這就是JavaScript中的函數被稱爲一等公民的緣由。
咱們能夠在JavaScript中將函數賦值給變量,例如:
const square = function(x) {
return x * x;
}
// prints 25
square(5);
複製代碼
咱們也能夠傳遞它們。例如:
const foo = square;
// prints 36
foo(6);
複製代碼
咱們能夠將函數做爲參數傳遞給其餘函數。例如:
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();
}
}
// prints 'What's up?'
greet('casual', formalGreeting, casualGreeting);
複製代碼
如今咱們知道了什麼是 first-class functions ,讓咱們深刻了解JavaScript中的高階函數。
高階函數是對其餘函數進行操做的函數,能夠將函數做爲參數傳遞或者返回一個函數。簡單來講,高階函數是一個函數,它接收函數做爲參數或將函數做爲輸出進行返回。
例如,Array.prototype.map
,Array.prototype.filter
和 Array.prototype.reduce
是語言中內置的一些高階函數。
讓咱們看一下內置高階函數的一些例子,看看它與咱們不使用高階函數的解決方案的比較。
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()
的回調函數接受三個參數: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沒有原生 map
方法。咱們能夠本身建立它,從而建立咱們本身的高階函數。
假設咱們有一個字符串數組,咱們但願將此數組轉換爲整數數組,其中每一個元素表示原始數組中字符串的長度。
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。
回調函數 fn
接收數組的當前元素並返回該元素的長度,該元素存儲在newArray中。for 循環完成後,返回 newArray
並將其賦值給 lenArray
咱們已經瞭解了高階函數和一些內置的高階函數。咱們還學習瞭如何建立本身的高階函數。簡而言之,高階函數是一個函數,能夠接收函數做爲參數,甚至能夠返回一個函數。高階函數就像常規函數同樣,具備接收和返回其餘函數的額外能力,即參數和輸出。