开发人员每天面临的几乎所有问题都可以通过解决一组较小的问题来解决,针对单个明确定义的问题的小解决方案。这些解决方案可以最好地描述为“纯函数”。
尽管这些函数中的大多数是在不同的库中实现的,但重要的是要了解如何以及何时将困难的问题分解为更小的问题。这种解决问题的思维方式将提高我们的开发效率,并使我们成为更好的开发人员。
今天的文章中,我分享的是我经常用来解决各种问题的 20 个有用的“纯函数”的无序集合,希望对你也有用。
下面,我们开始吧。
给定一个对象或数组,函数将返回指定路径的值,否则为 null。
const getValue = (obj, path) => path
.replace(/\[([^[\]]*)]/g, '.$1.')
.split('.')
.filter(prop => prop !== '')
.reduce((prev, next) => (
prev instanceof Object ? prev[next] : undefined
), obj);
getValue({ a: { b: c: 'd' } }, 'a.b.c'); // = d
getValue({ a: { b: c: [1, 2] } }, 'a.b.c[1]'); // = 2
确保值在指定范围内,否则“clamp”到最接近的最小值和最大值。
const clamp = (min, max, value) => {
if (min > max) throw new Error('min cannot be greater than max');
return value < min
? min
: value > max
? max
: value;
}
}
clamp(0, 10, -5); // = 0
clamp(0, 10, 20); // = 10
在执行下一个操作之前等待指定的持续时间(以毫秒为单位)。
const sleep = async (duration) => (
new Promise(resolve =>
setTimeout(resolve, duration)
)
);
await sleep(1000); // waits 1 sec
根据键控功能对对象中的相关项进行分组和索引。
const groupBy = (fn, list) => (
list.reduce((prev, next) => ({
...prev,
[fn(next)]: [...(prev[fn(next)] || []), next]
}), {})
);
groupBy(vehicle => vehicle.make, [
{ make: 'tesla', model: '3' },
{ make: 'tesla', model: 'y' },
{ make: 'ford', model: 'mach-e' },
]);
// {
// tesla: [ { make: 'tesla', ... }, { make: 'tesla', ... } ],
// ford: [ { make: 'ford', ... } ],
// }
根据键控功能创建包含相关项目的子列表。
import groupBy from './groupBy';
const collectBy = (fn, list) =>
Object.values(groupBy(fn, list));
collectBy(vehicle => vehicle.make, [
{ make: 'tesla', model: '3' },
{ make: 'tesla', model: 'y' },
{ make: 'ford', model: 'mach-e' },
]);
// [
// [ { make: 'tesla', ... }, { make: 'tesla', ... } ],
// [ { make: 'ford', ... } ],
// ]
获取列表的第一个元素。这个函数对于编写干净易读的代码很有用。
const head = list => list[0];
head([1, 2, 3]); // = 1
head([]); // = undefined
获取列表中除第一个元素之外的所有元素。这个函数对于编写干净易读的代码很有用。
const tail = list => list.slice(1);
tail([1, 2, 3]); // = [2, 3]
tail([]); // = []
通过递归地从嵌套子列表中提取所有项目来创建一个平面列表。
const flatten = list => list.reduce((prev, next) => ([
...prev,
...(Array.isArray(next) ? flatten(next) : [next])
]), []);
flatten([[1, 2, [3, 4], 5, [6, [7, 8]]]]); // = [1, 2, 3, 4, 5, 6, 7, 8]
查找键控函数定义的两个列表中存在的所有值。
const intersectionBy = (fn, listA, listB) => {
const b = new Set(listB.map(fn);
return listA.filter(val => b.has(fn(val)));
};
intersectionBy(v => v, [1, 2, 3], [2, 3, 4]); // = [2, 3]
intersectionBy(v => v, [{ a: 1 }, { a: 2 }], [{ a: 2}, { a: 3 }, { a: 4 }]); // = [{ a: 2 }];
通过键控函数确定的值对列表中的每个元素进行索引。
const indexBy = (fn, list) =>
list.reduce((prev, next) => ({
...prev,
[fn(next)]: next
}, {});
indexBy(val => val.a, [{ a: 1 }, { a: 2 }, { a: 3 }]);
// = { 1: { a: 1 }, 2: { a:2 }, 3: { a: 3 } }
查找第一个列表中不存在于第二个列表中的所有项目,由键控功能确定。
import indexBy from './indexBy';
const differenceBy = (fn, listA, listB) => {
const bIndex = indexBy(fn, listb);
return listA.filter(val => !bIndex[fn(val)]);
});
differenceBy(val => val, [1,2,3], [3,4,5]); // = [1,2]
differenceBy(
vehicle => vehicle.make,
[{ make: 'tesla' }, { make: 'ford' }, { make: 'gm' }],
[{ make: 'tesla' }, { make: 'bmw' }, { make: 'audi' }],
); // = [{ make: 'ford' }, { make: 'gm' }]
如果给定函数抛出错误,则返回默认值。
const recoverWith = async (defaultValue, fn, ...args) => {
try {
const result = await fn(...args);
return result;
} catch (_e) {
return defaultValue;
}
}
recoverWith('A', val => val, 'B'); // = B
recoverWith('A', () => { throw new Error() }); // = 'A'
计算两点 p1 和 p2 之间的距离。
const distance = ([x0, y0], [x1, y1]) => (
Math.hypot(x1 - x0, y1 - y0)
);
distance([0, 1], [5, 4]); // = 5.8309518948453
从列表中删除元素,从第一个元素开始,直到满足 som 谓词。
const dropWhile = (pred, list) => {
let index = 0;
list.every(elem => {
index++;
return pred(elem);
});
return list.slice(index-1);
}
dropWhile(val => (val < 5), [1,2,3,4,5,6,7]); // = [5,6,7]
给定一些产生每个元素的单独值的函数,计算列表中所有元素的总和。
const sumBy = (fn, list) =>
list.reduce((prev, next) => prev + fn(next), 0);
sumBy(product => product.price, [
{ name: 'pizza', price: 10 },
{ name: 'pepsi', price: 5 },
{ name: 'salad', price: 5 },
]); // = 20
给定一个评估函数,创建一个升序比较器函数。
const ascending = (fn) => (a, b) => {
const valA = fn(a);
const valB = fn(b);
return valA < valB ? -1 : valA > valB ? 1 : 0;
}
const byPrice = ascending(val => val.price);
[{ price: 300 }, { price: 100 }, { price: 200 }].sort(byPrice);
// = [{ price: 100 }, { price: 200 }, { price: 300 }]
给定一个评估函数,创建一个降序比较器函数。
const descending = (fn) => (a, b) => {
const valA = fn(b);
const valB = fn(a);
return valA < valB ? -1 : valA > valB ? 1 : 0;
}
const byPrice = descending(val => val.price);
[{ price: 300 }, { price: 100 }, { price: 200 }].sort(byPrice);
// = [{ price: 300 }, { price: 200 }, { price: 100 }]
在满足给定predicate的索引中找到第一个键值。
const findKey = (predicate, index) => Object
.keys(index)
.find(key => predicate(index[key], key, index));
findKey(
car => !car.available,
{
tesla: { available: true },
ford: { available: false },
gm: { available: true }
},
); // = "ford"
将给定列表的值拆分为两个列表,一个包含predicate函数评估为真值的值,另一个包含假值。
const bifurcateBy = (predicate, list) =>
list.reduce((acc, val, i) => (
acc[predicate(val, i) ? 0 : 1].push(val), acc),
[[], []]
);
bifurcateBy(val => val > 0, [-1, 2, -3, 4]);
// = [[2, 4], [-1, -3]]
执行从左到右的功能组合。所有额外的参数都将传递给列表中的第一个函数,因此可以有任何数量。结果将在第二个传递,第二个的结果将传递给第三个,……以此类推,直到处理完所有函数。
const pipe = (functions, ...args) => (
functions.reduce(
(prev, next) => Array.isArray(prev) ? next(...prev) : next(prev),
args
)
);
pipe([Math.abs, Math.floor, val => -val], 4.20); // = -4
pipe([(a, b) => a - b, Math.abs], 5, 10); // = 5
尽管所有这些功能对于帮助我们解决正在处理的问题非常有用,但最重要的是,我们要知道如何将复杂困难的问题进行分解,这样就可以独立解决每个明确的小问题。一旦你掌握了这个解决问题的小技巧,那么你已经在成为一名优秀的开发者的路上了!
来源: web前端开发
一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间
良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。
减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!
Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。
程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样
TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。
函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程
朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构
页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮
软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!