目录
- 1. times 更好的循环
- 2. cloneDeep 深度克隆JavaScript对象
- 3. attempt 去除try/catch
- 4. pick, omit, keys, values 对象切片
- 5. defaults defaultsDeep assign merge 对象合并
- 6. drop take dropWith takeWith 数组切片
- 7. chunk zip toParis fromPairs 数组分组
- 8. countBy partition 数组聚合
- 9. filter, reject 条件断言
- 10. min, max, minBy, maxBy 数据最大最小值
- 11. camelCase, kebabCase, lowerCase, upperCase, capitalize 字符串大小写
- 12. startsWith, endsWith, escape, unescape 字符串处理
- 13. forOwn, forEach 对象和数组遍历
- 14. 防抖(debounce)和节流(throttle)
- 15. curry, flow 柯里化和组合
Lodash已经为我做了很好的事情,它能让我的代码看起来更加的精简, 也提高了 应用程序逻辑的清晰度。此外,Lodash也迫使我们以函数式的方式来思考编 程。我们可以将应用切分为若干个小且专注的模块。这种模块化能够提高我们 应用程序在测试时的代码覆盖率。
1 times 更好的循环
for循环是一个常见的使用场景,但是它会污染附加变量作用域(scope)。 组 合使用数组和apply方法,我们可以实现N个循环,而无需创建一个额外的变量。 但是,这种写法不够直观。Lodash总的times方法就非常的直观易用。
_.times(5, function(){ ... });
2 cloneDeep 深度克隆JavaScript对象
深度克隆JavaScript对象是困难的,并且也没有什么简单的解决方案。你可以使 用奇特的原生解决方案: JSON.parse(JSON.stringify(objectToClone))进行深 度克隆。但是,这种方案仅在对象内部没有方法的时候才可行。
Lodash提供了cloneDeep方法来帮你简单的完成对象的深度克隆操作。当然, 使用clone你还能灵活的指定克隆的深度。
var objA = { "name": "colin" } var objB = _.cloneDeep(objA); objB === objA // false
3 attempt 去除try/catch
使用attempt可以去除try/catch代码,当函数出现错误时,返回一个error对象。 比如JSON.parse,通常一定要进行错误处理。未处理的JSON.parse错误就像一 个定时炸弹,不要假设你接收到的JSON对象是完全有效的。
let getHp = function(json) { let data = _.attempt(JSON.parse, json); if (data.hp) { return data; } return "Not Found"; }; console.log(getHp('{ "car": "miata", "hp": 150 }'));// 150 console.log(getHp('{}'));// Not Found console.log(getHp(''));// Not Found console.log(getHp({}));// Not Found
4 pick, omit, keys, values 对象切片
4.1 _.pick(object, [props])
创建一个从 object 中选中的属性的对象
console.log(_({a:1, b:'2', c:3 }).pick(['a', 'c']).value()); // => { 'a': 1, 'c': 3 }
4.2 _.omit(object, [props])
反向版pick; 这个方法一个对象,这个对象由忽略属性之外的object自身和 继承的可枚举属性组成。(注:可以理解为删除object对象的属性)
console.log(_({ 'a': 1, 'b': '2', 'c': 3 }).omit(['a', 'c']).value()); // => { 'b': '2' }
4.3 _.keys(object)
创建 object 自身可枚举属性名为一个数组。
console.log(_({a:1, b:2}).keys().value()); // [ "a", "b" ]
4.4 _.values(object)
创建 object 自身可枚举属性的值为数组
console.log(_({a:1, b:2}).values().value());
5 defaults defaultsDeep assign merge 对象合并
default分配来源对象的可枚举属性到目标对象所有解析为 undefined 的属性上。 来源对象从左到右应用。 一旦设置了相同属性的值,后续的将被忽略掉。
defaultsDeep和defaults 类似,不同的地方在于defaultsDeep遇到相同属性的时候,如果 属性值为纯对象(plain object)或者集合(collection)时,不是用后面的属性值 去覆盖前面的属性值,而是会把前后两个属性值合并。 如果源对象的属性值为 undefined,则会忽略该属性。
assign分配来源对象的可枚举属性到目标对象上。 来源对象的应用规则是从左 到右,随后的下一个对象的属性会覆盖上一个对象的属性。
merge 也和 assign 类似,不同的地方在于 merge 遇到相同属性的时候,如果 属性值为纯对象(plain object)或者集合(collection)时,不是用后面的属性值 去覆盖前面的属性值,而是会把前后两个属性值合并。 如果源对象的属性值为 undefined,则会忽略该属性。
defaults:前面的属性不为undefined且与后面属性名相同,后面的对象属性会 被忽略。defaultsDeep: 前面的属性不为undefined且与后面属性名 相同,后面的对象属性会被忽略。assign:后面的会覆盖前面的对 象属性,不会合并。merge: 后面的会覆盖前面的对象属性,会组合。
console.log(_.assign( {}, { a: 1 }, { b: { c: 2, d: 3} }, { b: { e: 4 } } )); // { a: 1, b: { e: 4 } } console.log(_.merge( {}, { a: 1 }, { b: { c: 2, d: 3} }, { b: { e: 4 } } )); // { a: 1, b: { c: 2, d: 3, e: 4 } } // 合并集合 let users = { 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] }; let ages = { 'data': [{ 'age': 36 }, { 'age': 40 }] }; console.log(_.merge({}, users, ages)); // { data: [ { user: 'barney', age: 36 }, { user: 'fred', age: 40 } ] }
6 drop take dropWith takeWith 数组切片
6.1 _.drop(array, [n=1])
创建一个切片数组,去除array前面的n个元素。(n默认值为1。)
_.drop([1, 2, 3]); // => [2, 3] _.drop([1, 2, 3], 2); // => [3]
6.2 _.take(array, [n=1])
创建一个切片数组,去除array前面的n个元素。(n默认值为1。)
_.take([1, 2, 3]); // => [1] _.take([1, 2, 3], 2); // => [1, 2]
6.3 .dropWhile(array, [predicate=.identity])
裁剪数组,起点从 predicate 返回假值开始。predicate 会传入3个参数:(value, index, array)。
let collection = [ { name: 'machine', grade: 'A+' }, { name: 'Jeannie', grade: 'B+' }, { name: 'Jeffrey', grade: 'C' }, { name: 'Carrie', grade: 'A-' }, { name: 'James', grade: 'A' } ]; console.log( _(collection) .dropWhile((item) => _.first(item.grade)==='A') .value() ); => 返回裁剪的数组,起点从grade的值不从A开始算起,即第二条记录,直至数组结束 (4) [{…}, {…}, {…}, {…}] 0: {name: "Jeannie", grade: "B+"} 1: {name: "Jeffrey", grade: "C"} 2: {name: "Carrie", grade: "A-"} 3: {name: "James", grade: "A"}
6.4 .takeWhile(array, [predicate=.identity])
从数组的开始提取数组,直到 predicate 返回假值。predicate 会传入三个参数:(value, index, array)。
let collection = [ { name: 'machine', grade: 'A+' }, { name: 'Jeannie', grade: 'B+' }, { name: 'Jeffrey', grade: 'C' }, { name: 'Carrie', grade: 'A-' }, { name: 'James', grade: 'A' } ]; console.log( _(collection4) .takeWhile((item) => _.first(item.grade)==='A') .value() ); => 从数组开始,直到grade的值不从A开始,只有一条记录 (1)[{…}] 0: {name: "Jeannie", grade: "B+"}
7 chunk zip toParis fromPairs 数组分组
7.1 _.chunk(array, [size=1])
将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。
_.chunk(['a', 'b', 'c', 'd'], 2); // => [['a', 'b'], ['c', 'd']] _.chunk(['a', 'b', 'c', 'd'], 3); // => [['a', 'b', 'c'], ['d']]
7.2 _.zip([arrays])
创建一个分组元素的数组,数组的第一个元素包含所有给定数组的第一个元素, 数组的第二个元素包含所有给定数组的第二个元素,以此类推。
_.zip([arrays]) _.zip(['fred', 'barney'], [30, 40], [true, false]); // => [['fred', 30, true], ['barney', 40, false]]
7.3 _.unzip(array)
这个方法类似于.zip,除了它接收分组元素的数组,并且创建一个数组,分组 元素到打包前的结构。(:返回数组的第一个元素包含所有的输入数组的第一元 素,第一个元素包含了所有的输入数组的第二元素,依此类推。)
let zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); // => [['fred', 30, true], ['barney', 40, false]] console.log(_(zipped).unzip().value()); // => [['fred', 'barney'], [30, 40], [true, false]]
7.4 _.toPairs(object)
创建一个object对象自身可枚举属性的键值对数组。这个数组可以通 过.fromPairs撤回。如果object 是 map 或 set,返回其条目。
console.log(_({a:1, b:2}).toPairs().value()); // => [['a', 1], ['b', 2]]
7.5 _.fromPairs(pairs)
与.toPairs正好相反;这个方法返回一个由键值对pairs构成的对象
console.log(_([['a', 1], ['b', 2]]).fromPairs().value()); // => { 'fred': 30, 'barney': 40 }
8 countBy partition 数组聚合
8.1 .countBy(collection, [iteratee=.identity])
创建一个组成对象,key(键)是经过 iteratee(迭代函数) 执行处理 collection中每个元素后返回的结果,每个key(键)对应的值是 iteratee(迭 代函数)返回该key(键)的次数(注:迭代次数)。 iteratee 调用一个参数: (value)。
_.countBy([6.1, 4.2, 6.3], Math.floor); // => { '4': 1, '6': 2 } // The `_.property` iteratee shorthand. _.countBy(['one', 'two', 'three'], 'length'); // => { '3': 2, '5': 1 }
8.2 .groupBy(collection, [iteratee=.identity])
创建一个对象,key 是 iteratee 遍历 collection(集合) 中的每个元素返回的 结果。 分组值的顺序是由他们出现在 collection(集合) 中的顺序确定的。每 个键对应的值负责生成 key 的元素组成的数组。iteratee 调用 1 个参数: (value)。
_.groupBy([6.1, 4.2, 6.3], Math.floor); // => { '4': [4.2], '6': [6.1, 6.3] } _.groupBy(['one', 'two', 'three'], 'length'); // => { '3': ['one', 'two'], '5': ['three'] }
8.3 .partition(collection, [predicate=.identity])
创建一个分成两组的元素数组,第一组包含predicate(断言函数)返回为 true(真值)的元素,第二组包含predicate(断言函数)返回为 false(假 值)的元素。predicate 调用1个参数:(value)。
let users2 = [ { 'user': 'barney', 'age': 36, 'active': false }, { 'user': 'fred', 'age': 40, 'active': true }, { 'user': 'pebbles', 'age': 1, 'active': false } ]; console.log(_.partition(users2, function(o) { return o.active; })); // [[{ 'user': 'fred', 'age': 40, 'active': true }], // [{ 'user': 'barney', 'age': 36, 'active': false }, // { 'user': 'pebbles', 'age': 1, 'active': false }]] console.log(_.partition(users2, { 'age': 1, 'active': false })); // [[{ 'user': 'pebbles', 'age': 1, 'active': false}], // [{ 'user': 'barney', 'age': 36, 'active': false }, // { 'user': 'fred', 'age': 40, 'active': true}]]
9 filter, reject 条件断言
9.1 .filter(collection, [predicate=.identity])
遍历集合中的元素,筛选出一个经过 predicate 检查结果为真值的数组, predicate 会传入3个参数:(value, index|key, collection)。
let collection = [ { name: 'machine', grade: 'A+' }, { name: 'Jeannie', grade: 'B+' }, { name: 'Jeffrey', grade: 'C' }, { name: 'Carrie', grade: 'A-' }, { name: 'James', grade: 'A' } ]; console.log( _(collection) .filter((item) => _.first(item.grade)==='A') .value() ); => 返回所有符合条件的记录组成的数据 (3) [{…}, {…}, {…}] 0: {name: "machine", grade: "A+"} 1: {name: "Carrie", grade: "A-"} 2: {name: "James", grade: "A"}
9.2 .reject(collection, [predicate=.identity])
_.filter的反向方法;此方法 返回 predicate(断言函数) 不 返回 truthy(真值)的collection(集合)元素(注释:非真)
let collection = [ { name: 'machine', grade: 'A+' }, { name: 'Jeannie', grade: 'B+' }, { name: 'Jeffrey', grade: 'C' }, { name: 'Carrie', grade: 'A-' }, { name: 'James', grade: 'A' } ]; console.log( _(collection) .reject((item) => _.first(item.grade)==='A') .value() ); => 返回所有不符合条件的记录组成的数据 (2) […] 0: Object { name: "Jeannie", grade: "B+" } 1: {…}
10 min, max, minBy, maxBy 数据最大最小值
10.1 _.min(array)
计算 array 中的最小值。 如果 array 是 空的或者假值将会返回 undefined。
_.min([4, 2, 8, 6]); // => 2 _.min([]); // => undefined
10.2 _.max(array)
计算 array 中的最大值。 如果 array 是 空的或者假值将会返回 undefine。
_.max([4, 2, 8, 6]); // => 8 _.max([]); // => undefined
10.3 .minBy(array, [iteratee=.identity])
这个方法类似 _.min 除了它接受 iteratee 来调用 array中的每一个元素,来 生成其值排序的标准。 iteratee 会调用1个参数: (value)
let objects = [{ 'n': 1 }, { 'n': 2 }]; _.minBy(objects, function(o) { return o.n; }); // => { 'n': 1 } // The `_.property` iteratee shorthand. _.minBy(objects, 'n'); // => { 'n': 1 }
10.4 .maxBy(array, [iteratee=.identity])
这个方法类似 _.max 除了它接受 iteratee 来调用 array中的每一个元素,来 生成其值排序的标准。 iteratee 会调用1个参数: (value) 。
var objects = [{ 'n': 1 }, { 'n': 2 }]; _.maxBy(objects, function(o) { return o.n; }); // => { 'n': 2 } // The `_.property` iteratee shorthand. _.maxBy(objects, 'n'); // => { 'n': 2 }
11 camelCase, kebabCase, lowerCase, upperCase, capitalize 字符串大小写
11.1 camelCase 函数可以将字符串中非数字和字母的字符都过滤掉,然后再转换为驼峰。
console.log(_.camelCase('Foo Bar')) // => 'fooBar' console.log(_.camelCase('--foo-bar--')) // => 'fooBar' console.log(_.camelCase('__FOO_BAR__')) // => 'fooBar' console.log(_.camelCase('/\__FOO_BAR__*\9')) // => 'fooBar9' console.log(_.camelCase('fooBarbar_bar')) // => fooBarbarBar
11.2 _.kebabCase([string=''])
转换字符串string为 kebab case.
_.kebabCase('Foo Bar'); // => 'foo-bar' _.kebabCase('fooBar'); // => 'foo-bar' _.kebabCase('__FOO_BAR__'); // => 'foo-bar'
11.3 _.lowerCase([string=''])
转换字符串string以空格分开单词,并转换为小写。
_.lowerCase('--Foo-Bar--'); // => 'foo bar' _.lowerCase('fooBar'); // => 'foo bar' _.lowerCase('__FOO_BAR__'); // => 'foo bar'
11.4 _.upperCase([string=''])
转换字符串string为 空格 分隔的大写单词。
_.upperCase('--foo-bar'); // => 'FOO BAR' _.upperCase('fooBar'); // => 'FOO BAR' _.upperCase('__foo_bar__'); // => 'FOO BAR'
11.5 capitalize 函数可以转换字符串 string 首字母为大写,剩下为小写。
console.log(_.capitalize('FRED')); // => 'Fred'
12 startsWith, endsWith, escape, unescape 字符串处理
12.1 startsWith 函数可以检查一个字符串是否以指定字符串开头。
_.startsWith('abc', 'a'); // => true _.startsWith('abc', 'b'); // => false
12.2 endsWith 函数可以检查一个字符串是否以指定字符串结尾。
_.endsWith('abc', 'c'); // => true _.endsWith('abc', 'b'); // => false
12.3 escape 函数可以转义 string 中的 &、<、>、"、' 和 ` 字符为 HTML 实体字符。
console.log(_.escape(`a as <a> &'"" *`)); // a as <a> &'"" *
12.4 unescape 函数的作用和 escape 正好相反,它可以转换HTML实体字符为string
console.log(_.unescape(`a as <a> &'"" *`)); //
13 forOwn, forEach 对象和数组遍历
13.1 .forOwn(object, [iteratee=.identity])
使用 iteratee 遍历自身的可枚举属性。 iteratee 会传入3个参数:(value, key, object)。 如果返回 false,iteratee 会提前退出遍历
console.log(_({a:1, b:2}).forOwn((value, key) => console.log(key))); // 输出 'a' 然后 'b' (无法保证遍历的顺序)
13.2 .forEach(collection, [iteratee=.identity])
调用 iteratee 遍历集合中的元素, iteratee 会传入3个参数:(value, index|key, collection)。 如果显式的返回 false ,iteratee 会提前退出。
_([1, 2]).forEach(function(value) { console.log(value); }); // => 输出 `1` 和 `2`
14 防抖(debounce)和节流(throttle)
14.1 debounce 防抖
如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。也就是说当调用 动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行 时间。
let debounce_fun = _.debounce(()=>console.log('function deboundced after 1000ms'), 1000); let loop_de = () => { setTimeout(loop_de, 100); debounce_fun(); }; loop_de(); // 永远不会输出'function deboundced after 1000ms'
14.2 throttle 节流
如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会 有一滴水流出。也就是会说预先设定一个执行周期,当调用动作的时刻大于等于 执行周期则执行该动作,然后进入下一个新周期。
let throttle_fun = _.throttle(()=>console.log('function throttle after 1000ms'), 1000); let loop_th = () => { setTimeout(loop_th, 100); throttle_fun(); }; loop_th(); // 每隔1S输出一次'function throttle after 1000ms'
15 curry, flow 柯里化和组合
15.1 curry 柯里化
其思想为,任何一个多参的“基础函数”,可以通过闭包等技术将该函数的前几个 参数赋值,从而将该函数“赋能”为“定制函数”。
var abc = function(a, b, c) { return [a, b, c]; }; var curried = _.curry(abc); curried(1)(2)(3); // => [1, 2, 3] curried(1, 2)(3); // => [1, 2, 3] curried(1, 2, 3); // => [1, 2, 3] // Curried with placeholders. curried(1)(_, 3)(2); // => [1, 2, 3]
15.2 flow 组合
其思想为将数据依次经过几个函数处理(函数的嵌套),最终输出结果数据。
const data = [ {name: 'a', age: 37, weight: 72}, {name: 'b', age: 17, weight: 64}, {name: 'c', age: 24, weight: 89}, {name: 'd', age: 66, weight: 83}, {name: 'e', age: 47, weight: 105}, {name: 'f', age: 18, weight: 61} ]; function filterBy(predicate, data) { return data.filter(predicate); } function sortBy(field, data) { return data.sort((a, b) => a[field] - b[field]); } function head(data) { return data[0]; } let flow_fun = _.flow([ _.curry(filterBy)(d => d.age > 20), // 筛选 20 岁以上的对象 _.curry(sortBy)('weight'), // 以 weight 进行排序 head ]); console.log(flow_fun(data));
Created: 2020-10-08 Thu 19:50