文章目录
高阶函数 (higher-order function)
一个普通函数:
function hello(arg) {
return value;
}
如果一个函数接收另一个函数(!)作为参数,或者一个函数返回一个新的函数(!),那么此函数为高阶函数。上面的例子,如果arg
或 value
为函数, 那么hello
函数为高阶函数。
1. 传递一个函数给函数
function sing(callback) {
console.log("la la la la");
if (callback instanceof Function) {
callback();
}
}
function meow() {
console.log("meow meow");
}
sing();
// expected output: la la la la
sing(meow);
// expected output:
// la la la la
// meow meow
下面的代码直接传递整个匿名函数定义给sing
函数:
sing(function() { console.log("meow meow"); });
2. 函数返回新的函数 (function factory 函数工厂)
function multiplier(factor) {
return function(x) {
return x * factor;
}
}
使用 arrow function,上面的代码简化为:
function multiplier(factor) {
return x => x * factor;
}
}
高阶函数multiplier
函数同时也是一个closure闭包函数,测试如下:
let doubler = multiplier(2);
> undefined
doubler;
> f(x) {
return x * factor;
}
doubler(6)
> 12
数组的高阶函数
数组可使用的高阶函数有很多,最常用的有如下几个函数:
1. map()
map
用于传递一个函数给数组,该函数应用于数组中的每一个元素,然后并不改变原来的数组,而是创建并返回一个新的数组:
let vals = [4, 8, 1, 2, 9];
console.log(vals); // 1
function doubler(x) {
return x*2;
}
let doubled = vals.map(doubler);
console.log(vals); // 2
console.log(doubled); // 3
使用arrow function 简化如下:
let vals = [4, 8, 1, 2, 9];
console.log(vals); // 1
let doubled = vals.map(x => x * 2); // 箭头函数
console.log(vals); // 2
console.log(doubled); // 3
输出如下:
(5) [4, 8, 1, 2, 9] // 1
(5) [4, 8, 1, 2, 9] // 2
(5) [8, 16, 2, 4, 18] // 3
由于需要创建新数组,所以performance不一定比得上使用for循环,但是使用高阶函数能使code看起来更简洁。
2. forEach()
forEach
和 map
都对数组中的每一个元素进行操作,都不改变原始数组。区别是: forEach
返回 undefined
, map
返回一个新数组。
forEach
典型应用场合:将数组中的每个元素存储到数据库中,不返回任何值:
chars = ['Hello' , 'world!!!'] ;
var retVal = chars.forEach(function(word){
console.log("Saving to db: " + word)
})
console.log(retVal) //undefined
运行结果:
Saving to db: Hello
Saving to db: world!!!
undefined
map
例子:
chars = ['Hello' , 'world!!!'] ;
var lengths = chars.map(function(word){
return word.length
})
console.log(lengths) // 新建数组: [5,8]
forEach
和 map
都不改变原始数组:
let a = [1, 2, 3];
a.forEach(i => i + 1);
let b = [1, 2, 3];
b.map(i => i + 1);
console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3]
下面的例子输出数组中的每一个元素:
let a = [1, 2, 3];
let x = a.forEach((i) => console.log(i));
console.log(x);
console.log("---------");
let b = [1, 2, 3];
const y = b.map((i) => console.log(i));
console.log(y);
运行结果:
1
2
3
undefined
---------
1
2
3
[ undefined, undefined, undefined ]
3. filter()
filter:过滤,对数组中的每一个元素进行筛选过滤,保留符合条件的元素。filter()
函数返回新的数组。
例 1
过滤掉数组中的奇数,保留偶数:
let vals = [5, 4, 9, 2 , 1];
function isEven(num) {
return num % 2 === 0;
}
vals = vals.filter(x => x % 2 === 0);
console.log(vals);
使用箭头函数:
let vals = [5, 4, 9, 2 , 1];
vals = vals.filter(x => x % 2);
console.log(vals);
输出结果:[4, 2]
例 2
将数组中的falsey
值过滤出去,仅保留truthy
值:
let vals = [5, 4, undefined, 9, 2 , 1];
vals = vals.filter(x => x);
console.log(vals);
输出:[5, 4, 9, 2, 1]
例 3
提取字符串中的单词:
let s = "It was a dark and stormy night.";
let words = s.split(" ");
console.log(words);
// expeted output: ["It", "was", "", "a", "dark", "and", "stormy", "night."]
was
与 a
之间有两个空格,所以结果数组多了一个空字符串,使用filter
函数过滤结果数组中的空字符串:
let s = "It was a dark and stormy night.";
let words = s.split(" ").filter(word => word.length);
console.log(words);
// expeted output: ["It", "was", "a", "dark", "and", "stormy", "night."]
4. reduce()
reduce
大意是说,将数组中的所有元素看作一个整体,找出实质,将数组精简(reduce)为单个输出。
例1
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
参数 accumulator
在对数组迭代的过程中一直存在。
例 2
不使用reduce
,下面的初始代码对数组求和:
let vals = [5, 4, 1, 2 , 9];
let sum = 0;
for (let i = 0; i < vals.length; i++) {
sum += vals[i];
}
console.log(sum);
使用 ES6 语法:
let vals = [5, 4, 1, 2 , 9];
let sum = 0;
for (let val of vals) {
sum += val;
}
console.log(sum);
如果使用reduce
, 代码如下:
let vals = [5, 4, 1, 2 , 9];
function sum(acc, val) {
return acc + val;
}
let answer = vals.reduce(sum);
console.log(answer);
如果将语句:
let answer = vals.reduce(sum);
修改为:
let answer = vals.reduce(sum + 10);
将输出31
, 这里 10
为指定初始值。如果不指定此值,初始值默认为数组的第1个元素。
使用 arrow function 改写code:
let vals = [5, 4, 1, 2 , 9];
let answer = vals.reduce((acc + val) => acc + val);
console.log(answer);
例 3
找出数组中最大的元素:
let vals = [5, 4, 1, 9 , 1];
function findMax(acc, val) {
if (acc < val) {
acc = val;
}
return acc;
}
let biggest = vals.reduce(findMax);
console.log(biggest);
使用 arrow function:
let vals = [5, 4, 1, 9 , 1];
let biggest = vals.reduce((acc, val) => {
if (acc < val) {
acc = val;
}
return acc;
});
console.log(biggest);
使用二元操作符进一步简化code:
let vals = [5, 4, 1, 9 , 1];
let biggest = vals.reduce((a, b) => a > b ? a : b);
console.log(biggest);
下面的代码返回20
,20
为初始值,找出数组中最大的元素, 如果没有元素的值大于20
, 则返回20
:
let vals = [5, 4, 1, 9 , 1];
let biggest = vals.reduce((a, b) => a > b ? a : b, 20);
console.log(biggest);
5. sort()
排序,默认以升序排列,从小到大,返回修改后的已存在的数组:
例 1
let vals = [5, 4, 9, 2, 1];
vals.sort();
console.log(vals);
// expected output: [1, 2, 4, 5, 9]
例 2
可以直接对字符串,字符等这些显而易见的元素排序,但是对对象数组排序无效:
let vals = [{x:4, y:3}, {x:1, y:7}];
console.log(vals);
// output: [{x:4, y:3}, {x:1, y:7}]
要对这种类型的值排序,需要自己提供比较函数给sort()
函数,因为这个原因,sort
是高阶函数。
let vals = [{x:2, y:10}, {x:5, y:6}];
function compare(a, b) {
return a.y - b.y;
}
vals.sort(compare);
console.log(vals);
// output: [{x:5, y:6}, {x:2, y:10}];
例 3
let s = "It was a dark and stormy night.";
let words = s.split(" ").filter(word => word.length);
console.log(words);
// expeted output: ["It", "was", "a", "dark", "and", "stormy", "night."]
对结果数组中的单词按字符串长度排序:
let s = "It was a dark and stormy night.";
let words = s.split(" ").filter(word => word.length);
words.sort((a, b) => (a.length - b.length));
console.log(words);
// expeted output: ["a", "It", "was", "and", "dark", "stormy", "night."]
6. every()
every()
方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。
const isBelowThreshold = (currentValue) => currentValue < 40;
const array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));
// Expected output: true
另一个数据库操作的例子:
// Find all users
const users = await User.findAll();
console.log(users.every(user => user instanceof User)); // true
数组函数 fill()
常用的函数还有一个fill()
,非高阶函数,直接使用静态值修改数组中的元素,然后返回修改后的数组:
语法:
fill(value)
fill(value, start)
fill(value, start, end)
例子:
const array1 = [1, 2, 3, 4];
// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// expected output: [1, 2, 0, 0]
// fill with 5 from position 1
console.log(array1.fill(5, 1));
// expected output: [1, 5, 5, 5]
console.log(array1.fill(6));
// expected output: [6, 6, 6, 6]
如下的code生成一个含有100个随机数元素的数组:
let vals = Array(100).fill().map(Math.random);
console.log(vals);
[1] https://www.youtube.com/watch?v=H4awPsyugS0