Lodash 是 JavaScript 社区最出名的一个工具库,提供了许多高效、高兼容性的工具函数。
不过,随着浏览器和 web 技术的发展,一些人开始反对在项目中使用 lodash。主要原因有:
本文来探讨一下,我们该不该在项目中使用 lodash,以及如何正确使用 lodash。是的,这个看起来有点标题党的标题,有两层含义:
个人认为,应该优先使用 ES 原生语法,同时,在大部分项目中仍然推荐使用 lodash 作为拓展工具库。原因如下:
不管怎么样,lodash 目前仍然保持着 4 千多万的周下载了,就足以见得它的流行程度。
即使你坚持不肯使用 lodash,我认为仍然有必要了解 lodash 提供了哪些功能,这些功能你会经常在开发中遇到。这个时候,你可以从 You Don't Need Lodash Underscore 中查看如何使用原生语法实现。
最常见的引入 lodash 的方式是:
// 方式1:引入整个lodash对象
import _ from "lodash";
// 方式2:按名称引入特定的函数
import { cloneDeep } from "lodash";
这两种方式都会引入整个 lodash 库。Lodash 含有许多函数,项目里一般只会用到其中的小部分,为了避免引入不必要的代码,lodash 提供了多种支持按需加载的方式。
插件 babel-plugin-lodash 和 lodash-webpack-plugin 能够在打包时去掉不必要的 lodash 代码,减小产物体积。
// 只引入 array 模块的功能
import array from "lodash/array";
// 只引入 cloneDeep 函数
import cloneDeep from "lodash/cloneDeep";
这种方式只会引入引用路径对应的模块,无需使用插件,也不会有冗余代码。缺点是每个 import 语句只能引入一个函数,可能导致多个 import 语句。
Lodash 为每个方法提供了单独的 npm 包,你可以只下载你想要的函数。
不推荐在项目中使用这种方式。首先,它并不像看起来一样轻量,lodash 中的公共代码会存在于每一个函数包中。其次,每个方法都是独立的依赖包,意味着多次安装、多个 package.json 依赖项、多个 node_modules 包目录。
Lodash 含有 Array, Collection, Date, Function, Lang, Math, Number, Object, String 等多个功能模块,总共几百个功能函数。官方文档上以字典顺序排序,不容易总结记忆。这里总结一些 ES6 不容易实现的实用功能。
String.toLowerCase/toUpperCase 只能进行简单大小写转换,lodash 还提供了
_.lowerFirst(string);
_.upperFirst(string);
// 第一个字符大写,其它字符小写
_.capitalize(string);
编程中,常见的多单词命名风格有:
除了大驼峰,其他三种风格都有对应的转换函数:
_.snakeCase(string);
_.kebabCase(string);
_.camelCase(string);
// 利用upperFirst和camelCase 实现 pascalCase
const pascalCase = (string) => _.upperFirst(_.camelCase(string));
// examples
_.snakeCase("fooBar"); // 'foo_bar'
_.camelCase("Foo Bar"); // 'fooBar'
_.kebabCase("__FOO_BAR__"); // 'foo-bar'
另外,还有两个不常用的全大写和全小写写法(以空格为分隔符),它们与 _.toLower/toUpper 的区别是会识别并转换字符串中的分隔符。
_.lowerCase(string);
_.upperCase(string);
// examples
_.lowerCase("--Foo-Bar--"); // 'foo bar'
_.upperCase("fooBar"); // 'FOO BAR'
算术运算:
// 求总和
_.sum(array);
// 求平均值
_.mean(array);
常用的数字操作:
// 返回一个[lower,upper]之间的随机数
// 如果lower和upper中有浮点数,或者floating为true,返回浮点数,否则,返回整数
_.random(lower=0,upper=1 [,floating])
// 生成一个范围数组
_.range([start=0,]end,step=1)
// 把一个数字就近限制在某个区间内
_.clamp(number,[lower=0,] upper)
// examples
_.clamp(-10, -5, 5); // -5
_.clamp(10, -5, 5); // 5
// 交集 intersection
_.intersection(...arrays);
_.intersectionWith(...arrays [, comparator]);
_.intersectionBy(...arrays [, iteratee]);
// 并集
_.union(...arrays);
_.unionWith(...arrays [, comparator]);
_.unionBy(...arrays [, iteratee]);
// 集合差,A - B 表示属于集合A但不属于集合B的元素集合
_.difference(array, ...operands);
_.differenceWith(array, ...operands [, comparator]);
_.differenceBy(array, ...operands [, iteratee]);
这三簇函数都不改变原来的数组,而是返回一个新的数组作为运算结果。其中,交并集的运算结果不含重复元素,集合差取决于第一个集合。
函数命名具有一定的规约,以交集为例:
_.intersection([2, 1, 1], [2, 3], [2, 4]);
// => [2]
const objects = [
{ x: 1, y: 2 },
{ x: 2, y: 1 },
];
const others = [
{ x: 1, y: 1 },
{ x: 1, y: 2 },
];
_.intersectionWith(objects, others, _.isEqual);
// => [{ 'x': 1, 'y': 2 }] , 结果引用objects中的元素
_.intersectionBy([2.1, 1.2], [2.3, 3.4], [3.2, 2.4], Math.floor);
// => [2.1]
分片(chunk)是指把数组中的每 n 个元素分为一组(一片),如果不能整除,最后剩下的元素单独一片。
_.chunk(array [, size=1])
// example
_.chunk(['a', 'b', 'c', 'd','e'], 2);
// => [["a", "b"], ["c", "d"], ["e"]]
分区(partition)是利用一个断言函数迭代每个元素,根据断言的 true 和 false,把元素分成两组。
_.partition(collection [, predicate])
// example
_.partition([4,5,6,7],num=>num>5)
// =>[[6, 7], [4, 5]]
分组(group) 则是用一个函数遍历每个元素,得到的结果作为该元素所在组的 key,相同 key 元素归为同一组。
_.groupBy(collection [, iteratee])
// example
_.groupBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }
在保证数组有序的情况下,查找和去重可以采用二分法,降低复杂度。Lodash 也提供了一些针对有序(升序)数组的操作。
sortedIndex / sortedLastIndex 可以操作基本的 number 数组和 string 数组:
// 返回插入该元素后仍然能保持数组有序的第一个下标位置
_.sortedIndex(array, value);
// 类似 sortedIndex,但返回最后一个能保持顺序的下标位置
_.sortedLastIndex(array, value);
// example
_.sortedIndex([1, 20, 20, 100, 500], 20); // 1
_.sortedLastIndex([1, 20, 20, 100, 500], 20); // 3
上面两个函数都只能在数字和字符串数组中使用,对于对象数组,可以用一个函数表示元素之间的排序依据:
// 以 iteratee 转化后的结果排序
_.sortedIndexBy(array, value [, iteratee])
_.sortedLastIndexBy(array, value [, iteratee])
// example
_.sortedIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, function(o) { return o.x; });
// => 0
注意,sortedIndex/sortedLastIndex 并不能直接用于元素查找,比如上面返回下标 3,但 array[3] 是 100 而不是 20。
有序数组查找用 sortedIndexOf/sortedLastIndexOf,它的功能与 indexOf/lastIndexOf 一样,不过采用了二分查找。
_.sortedIndexOf(array, value);
_.sortedLastIndexOf(array, value);
// example
_.sortedIndexOf([4, 5, 5, 5, 6], 5); //1
sortedUniq/sortedUniqBy 可以对有序数组去重。
_.sortedUniq(array)
_.sortedUniqBy(array [, iteratee])
// example
_.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
// => [1.1, 2.3]
// 随机返回一个元素
_.sample(collection)
// 随机返回n个元素
_.sampleSize(collection, [n=1])
// 打乱数组
_.shuffle(collection)
// 计数
_.countBy(collection [, iteratee])
_.countBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': 1, '6': 2 }
开发中经常需要从已有对象改造,得到我们期望的结构。Lodash 中有很多函数可以派上用场:
// 克隆
_.clone(value);
_.cloneWith(value, customizer);
_.cloneDeep(value);
_.cloneDeepWith(value, customizer);
// 同 Object.assign,把 sources 对象中的自有属性赋值到 object 中
_.assign(object, ...sources);
// 转化后赋值
_.assignWith(_.assignWith(object, sources, customizer));
// 类似_.assign, 但会赋值继承属性
_.assignIn(object, ...sources);
_.assignInWith(object, ...sources, customizer);
// 当object中不存在值时,才会赋值,经常用于合并默认值
_.defaults(object, ...sources);
// _.default 不适用多层对象,需要使用_.defaultsDeep
_.defaultsDeep(object, ...sources);
// 合并对象,类似 _.assign,但对象会递归深入,数组会被拼接
_.merge(object, ...sources);
_.mergeWith(object, ...sources);
注意,上面这些函数都会直接修改 object 参数。
与此不同,pick 和 omit 操作则返回新对象,不修改参数:
// 从对象中取出对应路径的值,合成一个新对象
_.pick(object, [paths]);
// 用一个断言函数决定要不要取这个属性,predicate(value,key)
_.pickBy(object, predicate);
// 去掉指定属性,把余下部分合成一个新对象,性能差于 pick
_.omit(object, [paths]);
_.omitBy(object, predicate);
// examples
_.pick({ a: 1, b: "2", c: 3 }, ["a", "c"]);
// => { 'a': 1, 'c': 3 }
_.pickBy({ a: 1, b: "2", c: 3 }, _.isNumber);
// => { 'a': 1, 'c': 3 }
_.omit({ a: 1, b: "2", c: 3 }, ["a", "c"]);
// => { 'b': '2' }
另外,对象还能像数组一样进行 map :
// iteratee(value,key,obj) 返回的结果作为新对象的key
_.mapKeys(object, iteratee);
// iteratee(value,key,obj) 返回的结果作为新对象的value
_.mapValues(object, iteratee);
// example
_.mapKeys({ a: 1, b: 2 }, function (value, key) {
return key + value;
});
// => { 'a1': 1, 'b2': 2 }
// 遍历自有属性,类似 for...in 加 hasOwnProperty判断。
_.forOwn(object, iteratee);
// 查找符合条件的key,类似数组的findIndex
_.findKey(object, iteratee);
_.findLastKey(object, iteratee);
// 查找对象中的函数属性
_.functions(object);
_.functionsIn(object);
在 JavaScript 中,读取和设置某一个路径下的值,是不安全的:
const object={a:1};
const=object.b.someKey; // TypeError: Cannot read properties of undefined
object.c.someKey=v; // TypeError: Cannot set properties of undefined
Lodash 为我们提供了更安全的 get 和 set 操作:
// 当path对应的值不存在时,返回undefined,而不是报错。
_.get(object,path [, defaultValue]);
// 一层层set,而不是报错
_.set(object,path,value);
// 根据该路径现在的value,更新为updater返回后的值,updater(value)=>newValue
_.update(object,path,updater)
尽管最近的 可选链 "?." 语法能取代 get 函数,但 set 操作依然没有较好的原生支持。
有时候,我们希望在特定条件下函数才执行,比如常见的防抖和节流:
_.debounce(func [, wait=0] [, options={}])
_.throttle(func [, wait=0] [, options={}])
// examples
// 只会在停住之后重新布局
window.addEventListener('resize', _.debounce(calculateLayout, 150));
// 会持续更新位置,但150ms更新一次,避免卡顿
window.addEventListener('resize', _.throttle(updatePosition, 150));
也可以根据调用次数控制是否执行操作。
// 函数只调用一次
_.once(func);
// 只在前n次调用
_.before(func, n);
// 只在n次之后才调用
_.after(func, n);
延迟执行:
// 在本次调用堆栈被清空后执行
_.defer(func, ...args);
// 等待wait ms 后执行,同setTimeout
_.delay(func, wait, ...args);
_.memorize 能缓存函数结果,避免重复计算,是一种常见的性能优化手段。
// resolver用于计算缓存key,当key相同时,使用缓存。默认使用func的第一个参数为key
_.memorize(func [, resolver])
// 柯里化
_.curry(func, (arity = func.length));
// 绑定部分参数,但不绑定this
_.partial(func, ...args);
// 只接收前n个参数,忽略额外参数
_.ary(func, n);
// 只接收第一个参数,同 _.ary(func,1)
_.unary(func);
流水线:
_.flow([funcs]);
const pascalCase = _.flow(_.upperFirst, _.camelCase);
生成一个唯一 ID:
_.uniqueId((prefix = ""));
作者:czpcalm
链接:https://juejin.cn/post/7143579596217122853
一项名为 JavaScript 功能的提案 Array.prototype.flatten 证明与 Web 不兼容。在 Firefox Nightly 中发布该功能会导致至少一个受欢迎的网站中断。鉴于有问题的代码是广泛使用的 MooTools 库的一部分,很可能会有更多网站受到影响。
让你了解到新兴和酷炫的web 开发趋势. 精心挑选发布一些认为大家值得关注的库:Direction Reveal、Carbon、Аxios、Jarvis、Toast UI Editor、Micron.js、lit
math.js是一个广泛应用于JavaScript 和 Node.js的数学库,它的特点是灵活表达式解析器,支持符号计算,内置大量函数与常量,并提供集成解决方案来处理不同的数据类型,如数字,大数字,复数,分数,单位和矩阵。
当你调研一个 JS 库,功能当然是最重要的。作者给出了从 12 个角度全面分析 JS 库的可用性,分别是:特性,稳定性,性能,包生态,社区,学习曲线,文档,工具,发展历史,团队,兼容性,趋势
对于今年的JavaScript状态调查,我想深入挖掘一下,不仅知道人们正在使用哪些工具和库,还要为什么他们选择使用它们。这意味着我必须找到一种方法将个人偏好转化为冷酷的数据。
本篇 React native 库列表不是从网上随便找的, 这些是我在我的应用中亲自使用的库。 这些库功能可能跟其它库也有,但经过大量研究并在我的程序中尝试后,我选择了这些库。
我们的使命是让您了解最新和最酷的Web开发趋势。下面为大家分享10个有趣的javascript和css库:Tessaract.js强大的javascript(节点和浏览器)库,用于从图像中提取文本。
在javascript的帮助下,可以为几乎每个领域(如移动、桌面应用程序和游戏Web开发)创建Web应用程序,因为该编程语言用作服务器端编程语言以实现最大功能,所以它是一种很好的编程语言。在本文中,我们选择了一些最佳的javascript库汇总
当我想要在网上找一个简洁的 Javascript 动效库时,总是发现很多“推荐”的库都是缺乏持续维护的。经过一番研究,我收集了 11 个最好的库,你可以用在自己的项目中。另外我还添加了一些有用但是缺少持续维护的库
如今 Vue 的热度不断攀升,使用者越来越多,Vue.js 工具也随处可见。这种现象不是凭空产生的:Vue 的学习曲线友好,功能驱动的结构清晰易懂,文档出色易学,所以新人入门很容易,经验丰富的开发人员也可以快速从其他框架
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!