全国统一服务热线:400-633-9193

如何在js代码中消灭for循环实例详解

    网络     2018-07-31    1386

前言

这篇文章基于我在公司内部分享会整理而成。欢迎探讨补充。

补充一:看来很多人没看完文章就评论了。我在文章末尾说了,是不写 for 循环,不是不用 for 循环。简单陈述不写 for 循环的理由:for 循环易读性差,而且鼓励写指令式代码和执行副作用。更多参考这篇文章

补充二:回应大家的一些反对意见。本来准备专门写文章回应的,但是没时间,就简短回复,直接扔链接了。

1、for 循环性能最好。回应:微观层面的代码性能优化,不是你应该关注的。我在文章中演示了,对百万级数据的操作,reduce 只比 for 循环慢 8 ms,可忽略不计。如果你要操作更大的数据,要考虑下换语言了。

  • Fast code is NOT important

  • The Sad Tragedy of Micro-Optimization Theater

  • Ditching the Micro-Optimization Fetish

2、不用 for 循环不能 break。回应:用递归。我在这篇文章里有解释怎样解决递归爆栈。

3、框架都用 for 循环!回应:框架考虑的场景和你不一样。React 和 Vue 还用 class 来创建对象呢。你该跟着学吗?事实上你应该用工厂函数。Class vs Factory function: exploring the way forward

一,用好 filter,map,和其它 ES6 新增的高阶遍历函数

问题一:

将数组中的空值去除

1
const arrContainsEmptyVal = [3, 4, 5, 2, 3, undefined, null, 0, ""];

答案:

1
const compact = arr => arr.filter(Boolean);

问题二:

将数组中的 VIP 用户余额加 10

1
2
3
4
5
const VIPUsers = [
 { username: "Kelly", isVIP: true, balance: 20 },
 { username: "Tom", isVIP: false, balance: 19 },
 { username: "Stephanie", isVIP: true, balance: 30 }
];

答案:

1
2
3
VIPUsers.map(
 user => (user.isVIP ? { ...user, balance: user.balance + 10 } : user)
);

问题三:

判断字符串中是否含有元音字母

1
const randomStr = "hdjrwqpi";

答案:

1
2
3
4
5
const isVowel = char => ["a", "e", "o", "i", "u"].includes(char);
const containsVowel = str => [...str].some(isVowel);
 
  
containsVowel(randomStr);

问题四:

判断用户是否全部是成年人

1
2
3
4
5
const users = [
 { name: "Jim", age: 23 },
 { name: "Lily", age: 17 },
 { name: "Will", age: 25 }
];

答案:

1
users.every(user => user.age >= 18);

问题五:

找出上面用户中的未成年人

答案:

1
2
3
const findTeen = users => users.find(user => user.age < 18);
 
findTeen(users);

问题六:

将数组中重复项清除

1
const dupArr = [1, 2, 3, 3, 3, 3, 6, 7];

答案:

1
2
3
const uniq = arr => [...new Set(arr)];
 
uniq(dupArr);

问题七:

生成由随机整数组成的数组,数组长度和元素大小可自定义

答案:

1
2
3
4
const genNumArr = (length, limit) =>
 Array.from({ length }, _ => Math.floor(Math.random() * limit));
 
genNumArr(10, 100);

二,理解和熟练使用 reduce

问题八:

不借助原生高阶函数,定义 reduce

答案:

1
2
3
4
5
const reduce = (f, acc, arr) => {
 if (arr.length === 0) return acc;
 const [head, ...tail] = arr;
 return reduce(f, f(head, acc), tail);
};

问题九:

将多层数组转换成一层数组

1
const nestedArr = [1, 2, [3, 4, [5, 6]]];

答案:

1
2
3
4
5
const flatten = arr =>
 arr.reduce(
 (flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next),
 []
 );

问题十:

将下面数组转成对象,key/value 对应里层数组的两个值

1
const objLikeArr = [["name", "Jim"], ["age", 18], ["single", true]];

答案:

1
2
3
4
const fromPairs = pairs =>
 pairs.reduce((res, pair) => ((res[pair[0]] = pair[1]), res), {});
 
fromPairs(objLikeArr);

问题十一:

取出对象中的深层属性

1
const deepAttr = { a: { b: { c: 15 } } };

答案:

1
2
3
4
const pluckDeep = path => obj =>
 path.split(".").reduce((val, attr) => val[attr], obj);
 
pluckDeep("a.b.c")(deepAttr);

问题十二:

将用户中的男性和女性分别放到不同的数组里:

1
2
3
4
5
6
const users = [
 { name: "Adam", age: 30, sex: "male" },
 { name: "Helen", age: 27, sex: "female" },
 { name: "Amy", age: 25, sex: "female" },
 { name: "Anthony", age: 23, sex: "male" },
];

答案:

1
2
3
4
5
6
7
8
9
10
const partition = (arr, isValid) =>
 arr.reduce(
 ([pass, fail], elem) =>
 isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]],
 [[], []],
 );
  
const isMale = person => person.sex === "male";
 
const [maleUser, femaleUser] = partition(users, isMale);

问题十三:

reduce 的计算过程,在范畴论里面叫 catamorphism,即一种连接的变形。和它相反的变形叫 anamorphism。现在我们定义一个和 reduce 计算过程相反的函数 unfold(注:reduce 在 Haskell 里面叫 fold,对应 unfold)

1
2
3
4
5
6
7
const unfold = (f, seed) => {
 const go = (f, seed, acc) => {
 const res = f(seed);
 return res ? go(f, res[1], acc.concat(res[0])) : acc;
 };
 return go(f, seed, []);
};

根据这个 unfold 函数,定义一个 Python 里面的 range 函数。

答案:

1
2
const range = (min, max, step = 1) =>
 unfold(x => x < max && [x, x + step], min);

三,用递归代替循环

问题十四:

将两个数组每个元素一一对应相加

1
2
const num1 = [3, 4, 5, 6, 7];
  分享到:  
0.2685s