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
咱们已经了解了高阶函数和一些内置的高阶函数。咱们还学习了如何建立本身的高阶函数。简而言之,高阶函数是一个函数,能够接收函数做为参数,甚至能够返回一个函数。高阶函数就像常规函数同样,具备接收和返回其余函数的额外能力,即参数和输出。