纯牛奶645
纯牛奶645
  • 发布:2018-02-11 16:39
  • 更新:2018-02-11 16:39
  • 阅读:5156

lodash

分类:Native.js

一、什么是lodash?
lodash库是一个具有一致接口、模块化、高性能等特性的JavaScript工具库。
lodash是一个JavaScript库,也是NodeJS的常用模块,它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数,其中部分是目前ECMAScript尚未制定的规范,但同时被业界认可的辅助函数。
二、lodash模块组成
Array,适用于数组类型,比如填充数据、查找元素、数组分片定操作;
Collection,适用于数组和对象类型,部分适用于字符串,比如分组、查找、过滤等操作;
Function,适用于函数类型,常用于执行类型判断和类型转换;
Lang,普遍适用于各种类型,常用于执行类型判断和类型转化;
Math,适用于数值类型,常用于执行数学运算;
Number,适用于生成随机数,比较数值与数值区间的关系;
Object,适用于对象类型,常用于对象的创建、扩展、类型转换、检索、集合等操作;
Seq,常用于创建链式调用,提高执行性能(惰性计算);
String,适用于字符串类型;
三、安装及使用
以 _.groupBy()方法为例来讲:
(一)使用方法
1.安装命令: npm i --save lodash
2.使用方法;

 import  _ from ‘lodsh’;  
 let names  = require('./names');  
 names = _.groupBy(require('./names'),(name) => name[0].toUpperCase( ));

(二)参数详细介绍
.groupBy(collection,[iteratee = .identity])

例子

五、举个“栗”子
我们要实现分组的城市列表,类似于微信的通信录列表,上张图:

假设我们现在只有这样的数据:

那怎么实现呢?用groupBy


import _ from 'lodash';  
let cities = reuqire('./beforeCity.json');  
...  
getCityInfo( ){  
        console.log('cities=',cities);  
        let cityList = [ ];  
        cityList = _.groupBy(cities, (city) => city.pinyin[0]);  
        console.log('cityList=',cityList);  
}

分组结果如下:

这样分组之后就很方便了

作者:sybil052
链接:https://www.jianshu.com/p/428b5b2fd913
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Lodash
有多年开发经验的工程师,往往都会有自己的一套工具库,成为utils、helpers等等,这套库一方面是自己的技术积累,另一方面也是对某项技术的扩展,领先于技术规范的制定的实现。
Lodash 就是这样的一套工具库,它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数,其中部分是目前 ECMAScript 尚未制定的规范,但同时被业界所认可的辅助函数。目前每天使用 npm 安装 Lodash 的数量在百万级以上,这在一定程度上证明了其代码的健壮性,值得我们在项目中一试。

模块组成
Lodash 提供的辅助函数主要分为以下几类:
Array,适用于数组类型,比如填充数据、查找元素、数组分片等操作
Collection,适用于数组和对象类型,部分适用于字符串,比如分组、查找、过滤等操作
Function,适用于函数类型,比如节流、延迟、缓存、设置钩子等操作
Lang,普遍适用于各种类型,常用于执行类型判断和类型转换
Math,适用于数值类型,常用于执行数学运算
Number,适用于生成随机数,比较数值与数值区间的关系
Object,适用于对象类型,常用于对象的创建、扩展、类型转换、检索、集合等操作
Seq,常用于创建链式调用,提高执行性能(惰性计算)
String,适用于字符串类型
lodash/fp 模块提供了更接近函数式编程的开发方式,其内部的函数经过包装,具有 immutable、auto-curried、iteratee-first、data-last(官方介绍)等特点。Lodash 在 GitHub Wiki 中对 lodash/fp 的特点做了如下概述:
Fixed Arity,固化参数个数,便于柯里化
Rearragned Arguments,重新调整参数位置,便于函数之间的聚合
Capped Iteratee Argument,封装 Iteratee 参数
New Methods
In functional programming, an iteratee is a composable abstraction for incrementally processing sequentially presented chunks of
input data in a purely functional fashion. With iteratees, it is possible to lazily transform how a resource will emit data, for example,
by converting each chunk of the input to uppercase as they are retrieved or by limiting the data to only the five first chunks without
loading the whole input data into memory. Iteratees are also responsible for opening and closing resources, providing predictable
resource management. ———— iteratee, wikipedia


// The `lodash/map` iteratee receives three arguments:  
// (value, index|key, collection)  
_.map(['6', '8', '10'], parseInt);  
// → [6, NaN, 2]  

// The `lodash/fp/map` iteratee is capped at one argument:  
// (value)  
fp.map(parseInt)(['6', '8', '10']);  
// → [6, 8, 10]  

// `lodash/padStart` accepts an optional `chars` param.  
_.padStart('a', 3, '-')  
// → '--a'  

// `lodash/fp/padStart` does not.  
fp.padStart(3)('a');  
// → '  a'  
fp.padCharsStart('-')(3)('a');  
// → '--a'  

// `lodash/filter` is data-first iteratee-last:  
// (collection, iteratee)  
var compact = _.partial(_.filter, _, Boolean);  
compact(['a', null, 'c']);  
// → ['a', 'c']  

// `lodash/fp/filter` is iteratee-first data-last:  
// (iteratee, collection)  
var compact = fp.filter(Boolean);  
compact(['a', null, 'c']);  
// → ['a', 'c']

在React+Webpack+Babel(ES6)的开发环境中,使用Lodash需要安装插件babel-plugin-lodash并更新Babel配置文件:
npm install --save lodash
npm install --save-dev babel-plugin-lodash
更新Babel的配置文件 .babelrc:
{
"presets": [
"react",
"es2015",
"stage-0"
],
"plugins": [
"lodash"
]
}
使用方式:
import from 'loadsh';
import {add} from 'lodash/fp';
const addOne = add(1);
.map([1,2,3],addOne);
性能

在 Filip Zawada 的文章《How to Speed Up Lo-Dash ×100? Introducing Lazy Evaluation》 中提到了 Lodash 提高执行速度的思路,主要有三点:Lazy Evaluation、Pipelining 和 Deferred Execution。下面两张图来自 Filip 的博客:

假设有如上图所示的问题:从若干个球中取出三个面值小于 10 的球。第一步是从所有的球中取出所有面值小于 10 的球,第二步是从上一步的结果取三个球。

上图是另一种解决方案,如果一个球能够通过第一步,那么就继续执行第二步,直至结束然后测试下一个球……当我们取到三个球之后就中断整个循环。Filip 称这是 Lazy Evaluation Algorithm,就个人理解这并不全面,他后续提到的 Pipelining(管道计算),再加上一个中断循环执行的算法应该更符合这里的图示。
此外,使用 Lodash 的链式调用时,只有显示或隐式调用 .value 方法才会对链式调用的整个操作进行取值,这种不在声明时立即求值,而在使用时求值的方式,是 Lazy Evaluation 最大的特点。
九个实例

受益于 Lodash 的普及程度,使用它可以提高多人开发时阅读代码的效率,减少彼此之间的误解(Loss of Consciousness)。在《Lodash: 10 Javascript Utility Functions That You Should Probably Stop Rewriting》一文中,作者列举了多个常用的 Lodash 函数,实例演示了使用 Lodash 的技巧。

  1. N 次循环
    //1.Basic for loop  
    for(var i = 0;i < 5; i++) {  
    //...  
    }  
    //2.Using Array's join and split methods  
    Array.apply(null,Array(5).forEach(function ( ) {  
      //...  
    });  
    //lodash 一个lodash解决问题  
    _.tiem(5,function ( ) {  
     //...  
    });

    for 语句是执行循环的不二选择,Array.apply 也可以模拟循环,但在上面代码的使用场景下, _.times( )的解决方式更加简洁和易于理解。
    2.深层查找属性值

    
    //Fetch the name of the first pet from each owner  
    var ownerArr = [{  
         "owner": "Colin",  
          "pets":[{"name":"dog1"},{"name":"dog2"}]  
    },{  
    "owner":"John",  
    "pets":[{"name":"dog3"},{"name":"dog4"}]  
    }];  
    //Array's map method  
    ownerArr.map(function(owner){  
    return owner.pets[0].name;  
    });  

// Lodash
_.map(ownerArr, 'pets[0].name');


_.map方法是对原生map方法的改进,其中使用pet[0].name字符串对嵌套数据取值的方式简化了很多冗余的代码,非常类似使用jQuery选择DOM节点 ul > li > a,对于前端开发者来说有种久违的亲切感。  

3.个性化数组  
 ```javascript  
//Array‘s map method  
Array.apply(null,Array(6)).map(function(item,index){  
      return "ball_" + index;  
});  
//lodash  
_.times(6,_.uniqueId.bind(null,'ball_'));  
//lodash  
_.times(6,_.partial(_.uniqueId,'ball_'));  
// eg. [ball_0, ball_1, ball_2, ball_3, ball_4, ball_5]

在上面的代码中,我们要创建一个初始值不同、长度为6的数组,其中_.uniqueId方法用于生成独一无二的标识符(递增的数字,在程序运行期间保持独一无二),_partial方法是对bind的封装。
4.深拷贝

var objA = {  
     "name": "colin"  
}  
var objB = _.cloneDeep(objA);  
objB === objA //false

javaScript没有直接提供深拷贝的函数,但我们可以用其他函数来模拟,比如 JSON.parse(JSON.stringfy(objectToClone)),但这种方法要求对象中属性值不能是函数。Lodash中的_.cloneDeep函数封装了深拷贝的逻辑,用起来更加简洁。
5.随机数

//Native utility method  
function getReandomNumber (min,max) {  
    return Math.floor(Math.random( ) * (max - min + 1)) +min;  
}  
getRandomNumber(15,20);  
//Lodash  
_.random(15,20);

Lodash的随机数生成函数更贴近实际开发,ECMAScript的随机数生成函数是底层必备的接口,两者都不可或缺。此外,使用_.random(15,20,true)还可以在15到20之间生成随机的浮点数。
6.对象扩展

//Adding extend function to Object.prototype  
Object.prototype.extend = function(obj) {  
         for(var i in obj) {  
              if(obj.hasOwnProperty(i)) {  
    this[i] = obj[i];  
}  
      }  
};  
var objA = {“name”:"colin","cat":"suzuki"};  
var objB = {"name":"james","age":17};  
ojbA.extend(objB);  
objA; //{"name":"james","age":17,"cat","suzuki"};  
//Lodash  
_.assign(objA,objB);

_.assign是浅拷贝,和ES6新增的Object.assign函数功能一致(建议优先使用Object.assign).
多层嵌套如果解决,如jquery的$.extend(true,{});
7.筛选属性

//Naive method:Remove an array of keys from object  
Object.prototype.remove = function(arr) {  
       var that = this;  
       arr.forEach(function(key){  
            delete(that[key]);  
});  
};  
var objA = {"name":"colin","car":"suzuki","age":17};  
//将remove函数挂载在Object原型链上,在此处就可以使用了  
objA.remove(['car','age']);  
objA; // {"name":"colin"}  
//Lodash  
objA = _.omit(objA,['car','age']);  
//=> {"name": "colin"}  
//Lodash  
objA = _.omit(objA,['car','age']);  
//=> {"name":"colin"}  
objA = _.omit(objA,'car');  
// => {"name": "colin", "age": 17};  
objA = _.omit(objA,_.isNumber);  
//_.isNumber是什么函数  
// => {"name": "colin"};

大多数情况下,lodash所提供的辅助函数都会比原生的函数更贴近开发需求。在上面的代码中,开发者可以使用数组、字符串以及函数的方式筛选对象的属性,并且最终会返回一个新的对象,中间执行筛选时不会对旧对象产生影响。

//Naive method:Returning a new object with selected properties  
Object.prototype.pick = function(arr) {  
     var _this = this;  
    var obj = { };  
    arr.forEach(function(key){  
         obj[key] = _this[key];  
});  
return obj;  
}  

var objA = {"name": "colin", "car": "suzuki", "age": 17};  
var objB = objA.pick{['car','age']};  
//{"car":"suzuki","age":17}  
//Lodash  
var objB = _.pick(objA,['car','age']);  
//{"car":"suzuki","age":17}

.pick是.omit的相反操作,用于从其他对象中挑选属性生成新的对象。
8.随机元素

var luckDraw = ["Colin","John","James","Lily","Mary"];  
function pickRandomPerson(luckyDraw){  
      var index = Math.floor(Math.random() * (luckyDraw.length - 1));  
      return luckyDraw[index];  
}  
pickRandomPerson(luckyDraw);//John  
//Lodash  
_.sample(luckyDraw,2);//Colin  
// Lodash - Getting 2 random item  
_.sample(luckyDraw, 2); // ['John','Lily']  //随机挑选两个元素并返回新的数组

_.sample 支持随机挑选多个元素并返回新的数组。

9.针对JSON.parse的错误处理

//using try-catch to handle the JSON.parse error  
function parse (str) {  
     try {  
          return JSON.parse(str);  
}  
catch(e){  
         return false;  
  }  
}  
// With Lodash  
function parseLodash(str) {  
     return _.attempt(JSON.parse.bind(null,str));  
}  
parse('a');  
//=>false  
parseLodash('a');  
// =>Return an error object  
parse('{"name":"colin"}');  
// => Return {"name":"colin"}  
parseLodash('{"name":"colin"}');  
// => Return {"name":"colin"}

如果你在使用JSON.parse时没有预置错误处理,那么它很有可能会成为一个定时炸弹,我们不应该默认接收的JSON对象都是有效的。try-catch是最常见的错误处理方式,如果项目中Lodash,那么可以使用_.attmpt替代try-catch的方式,当解析JSION出错时,改方法会返回一个Error对象。

随着 ES6 的普及,Lodash 的功能或多或少会被原生功能所替代,所以使用时还需要进一步甄别,建议优先使用原生函数,有关 ES6 替代 Lodash 的部分,请参考文章《10 Lodash Features You Can Replace with ES6》(中文版《10 个可用 ES6 替代的 Lodash 特性》)。
其中有两处非常值得一看:

//使用箭头函数创建可复用的路径:  
const object = { 'a': [ {'b': {'c':3} },4] };  
[   
obj => obj.a[0].b.c,   
obj => obj.a[1]   
].map(path => path(object));  
//使用箭头函数编写链式调用  
const pipe = functions => data => {  
      return functions.reduce(  
             (value,func) => func(value),  
            data  
     );  
};  

const pipeline = pipe([  
     x => x * 2,  
     x => x / 3,  
     x => x > 5,  
     b => !b  
]);  

pipeline(5);  
//true  
pipeline(20);  
//false

在ES6中,如果一个函数只接受一个形参且函数体是一个return语句,就可以使用箭头函数简化为:

const func = p => v;  
//类似于(不完全相同)  
const func = function (p) {  
     return v;  
}

当有多重嵌套时,可以简化为:

const  func = a => b => c => a+b+c;  
func(1)(2)(3);  
// => 6  
//类似于  
const func = function (a){  
        return function (b) {  
                return function (c) {  
                return a + b + c;   
        }  
    }  
}

const objects = {'a':[{'b':{'c':3}},4]};
// 这种写法存在问题吗??????map出现的结果为什么是undefined
[function (obj){
obj.a[0].b.c
},function(obj){
obj.a[1]
}].map(function (path) {
console.log(path(objects));
path(objects);
});
//使用箭头函数编写链式调用
const pipe = functions => data => {
return functions.reduce(
(value,func) => func(value),
data
);
};

const pipe = function functions(){
return function data() {
return functions.reduce(
function(value,func){
func(value);
}
)
}
}

const pipeline = pipe([
x => x * 2,
x => x /3,
x => x > 5,
b => !b
]);

作者:pinggod
链接:https://www.jianshu.com/p/7436e40ac5d1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关文章:https://www.zhihu.com/question/36942520

10 个可用 ES6 替代的 Lodash 特性
参考来源:http://www.zcfy.cc/article/10-lodash-features-you-can-replace-with-es6-467.html
Lodash现在是npm上被依赖最多的包,如果你现在使用ES6的话,实际上你可能不再需要它了。

1.Map,Filter,Reduce
这些集合方法使数据转化变得轻而易举。由于普遍地对此特性的支持,我们可以将它与箭头函数组合起来,以助我们使用比 Lodash 的实现更简便的方法来实现。

_.map([1,2,3],function(n){  return n * 3; });  
// [3, 6, 9]  
_.reduce([1, 2, 3], function(total, n) { return total + n; }, 0);  
// 6  
_.filter([1, 2, 3], function(n) { return n <= 2; });  
// [1, 2]  
//变为  
[1,2,3].map(n => n * 3);  
[1,2,3].reduce((total,n) => total + n);  
[1, 2, 3].filter(n => n <= 2);  

不止于此,如果我们使用 ES6 的 polyfill,我们也能使用 find、some、every 和 reduceRight

  1. Head & Tail
    解构语法 让我们可以获取一个列表的头(head)和尾(tail),而无需工具函数。
    
    _.head([1, 2, 3]);  
    // 1  
    _.tail([1, 2, 3]);  
    // [2, 3]  
    //变为  
    const [head, ...tail] = [1, 2, 3];  
也可以用相似的方式获得列表最后一个元素(last)以及除其之外的元素(initial)
```javascript  
_.initial([1, 2, 3]);  
// -> [1, 2]  
_.last([1, 2, 3]);  
// 3  

// 变为  

const [last, ...initial] = [1, 2, 3].reverse();  

如果你讨厌 reverse 会改变数据结构,那你可以在调用 reverse 之前使用延展操作符(spread operator)来复制一个数组。

const xs = [1,2,3];  
const [last,...initial] = [...xs].reverse( );  

3.Rest & Spread
rest 和 spread函数能让我们定义与调用能接受参数数量不定的函数。es6为这两种操作引入了专用的语法。

var say = _.rest(function (what,names) {  
    var last = _.last(names);  
   var initial = _.initial(names);  
    var finalSeparator = (_.size(names) > 1 ? ', &' : ' ');  
   return what + '  ' + initial.join(', ') + finalSeparator + _.last(names);  
});  
//what代表实参‘hello’,names代表实参'fred','barney','pebbles'  
say('hello','fred','barney','pebbles');  
// "hello fred, barney, & pebbles"  

//变为  
const say = (what,...names)=> {  
       const [last,...initial] =names.reverse( );  
       const finalSeparator = (names.length > 1 ? ', &' : ' ');  
       return `${what} ${initial.join('. ')} ${finalSeparator} ${last}`;  
};  
say("hello","fred","barney","pebbles");  
// "hello fred, barney, & pebbles"
  1. Curry
    如果没有更高级的语言如TypeScript 和 Flow 的支持,我们不能给函数设置类型签名,这使得函数的柯里化(currying)非常困难。当我们接收一个柯里化的函数时,很难知道已经应用了多少参数以及我们接下来该提供什么参数。通过箭头函数,我们能显示地定义柯里化函数,使得它们对其他程序员来说非常易于理解。
    function add(a,b) {  
    return a + b;  
    }  
    var curriedAdd = _.curry(add);  
    var add2 = curriedAdd(2);  
    add2(1); //3  
    //变为  
    const add = a => b => a + b;  //需要再思考一下  
    const add2 = add(2);  
    add2(1); //3

    这些显式的柯里化箭头函数对调试非常重要。

    
    var lodashAdd = _.curry(function(a,b) {  
      return a + b;  
    });  
    var add3 = lodashAdd(3);  
    console.log(add3.length);  
    //0  
    console.log(add3);  
    // function wrapper() {  
    //     var length = arguments.length,  
    //     args = Array(length),  
    //     index = length;  

// while (index--) {
// args[index] = arguments[index];
// }
// }
//变为
const es6Add = a => b => a + b;
const add3 = es6Add(3);
console.log(add3.length);
//1
console.log(add3);
// function b => a + b

如果我们使用函数式的库如 lodash/fp 和 ramda,那我们也能使用箭头函数来免除对自动柯里化风格的需要。
```javascript  
_.map(_.prop('name'))(people);  
//变为  
people.map(person => person.name);

5.Partial
正如柯里化一样,我们也能使用箭头函数来简化显示化偏函数用法。

var greet = function(greeting, name) {  
        return greeting + '  ' + name;  
};  
var sayHelloTo = _.partial(greet,'hello');  
sayHelloTo('fred');  
// "hello fred"  
//变化  
const sayHelloTo = name => greet('hello',name);  
sayHelloTo('fred');  
//"hello fred"

我们也能结合剩余参数与延展操作符来部分地应用可变参数函数

const sayHelloTo = (name, ...args) => greet('hello',name, ...args);  
sayHelloTo('fred',1,2,3);  
// 'hello fred'
  1. Operators
    Lodash把很多句法操作符重新实现成了函数,所以他们可以被传入集合方法。
    在大多数情况下,箭头函数能让它们定义地足够简单精炼,一行足矣。
    _.eq(3,3);  
    //true  
    _.add(10,1);  
    //11  
    _.map([1,2,3],function(n) {  
    return _.multiply(n,10);  
    });  
    // [10,20,30]  
    _.reduce([1,2,3], _.add);  
    //6  
    //变为  
    3 === 3  
    10 + 1  
    [1,2,3].map(n => n * 10);  
    [1,2,3].reduce((total,n) => total + n);

    7.Paths
    许多Lodash的函数把路径当做字符串或者数组。然而我们可以使用箭头函数来创建更多可重用的路径。

    var object = {'a':[{'b':{'c':3}},4]};  
    _.at(object,['a[0].b.c','a[1]']);  
    // [3,4]  
    _.at(['a','b','c'],0,2);  
    // ['a','c']  
    //变为  
    [  
    obj => obj.a[0].b.c,  
    obj => obj.a[1]  
    ].map(path => path(object));  
    [  
    arr => arr[0],  
    arr => arr[2]  
    ].map(path => path(['a','b','c']));

    因为这些路径“只是函数”,所以我们也能复合他们。

    const getFirstPerson = people => people[0];  
    const getPostCode = person => person.address.postcode;  
    const getFirstPostCode = prople => getPostCode(getFirstPerson(people));  
    我们甚至能创建更高阶能接收参数的路径。  
    const getFirstNPeople = n => people => people.slice(0,n);  
    const getFirst5People = getFirstNPeople(5);  
    const getFirst5PostCodes = people => getFirst5People(people).map(getPostCode);

    8.Pick
    pick工具能让我们从一个对象中选择我们想要的属性。我们也能通过解构与对象字面量简写来获取同样的结果。

    var object = {‘a’:1,'b':2,'c':3};  
    return _.pick(object,['a','c']);  
    //{a:1,c:3}  
    //变为  
    const {a,c} = {a:1,b:2,c:3};  
    return {a,c};

    9.Constant,Identity,Noop
    Lodash提供了一些工具函数来创建简单的具有某一特定行为的函数。

    _.constant({'a':1})( );  
    //{a:1}  
    _.identify({user:'fred'});  
    //{user:'fred'}  
    _noop( );  
    //undefined

    我们也能使用箭头函数行内定义这些函数。

    const constant = x => () => x;  
    const identity = x => x;  
    const noop = () => undefined;

    或者我们也能把上面的例子重写为:

    (() => ({ a: 1 }))();  
    // { a: 1 }  
    (x => x)({ user: 'fred' });  
    // { user: 'fred' }  
    (() => undefined)();  
    // undefined

    10.Chaining &Flow
    Lodash 提供了一些函数来帮我们编写链式的语句。在大多情况下,内置的集合函数会返回一个数组实例,能直接被链式调用。但某些情况下,这些方法会改变这个集合,这样就不可能再直接地链式调用了(译者注:需要自己返回实例)。
    然而我们也能以一个箭头函数数组来定义同样的转化。

    
    _([1, 2, 3])  
    .tap(function(array) {  
    // 修改输入函数  
    array.pop();  
    })  
    .reverse()  
    .value();  
    // [2, 1]  

// 变为

const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];

pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);

这样的话,我们甚至不需要去思考 tap 和 thru 的不同。把这个归约包装成工具函数能创造出一个非常有用的多功能工具。
```javascript  
const pipe = functions => data => {  
  return functions.reduce(  
    (value, func) => func(value),  
    data  
  );  
};  

const pipeline = pipe([  
  x => x * 2,  
  x => x / 3,  
  x => x > 5,  
  b => !b  
]);  

pipeline(5);  
// true  
pipeline(20);  
// false

总结
Lodash 仍然还是一个非常优秀的库,这篇文章只提供了一个新鲜的观点,JavaScript 的进化版是如何让我们在一些我们之前可能不得不依赖一些工具模块的场景里允许我们直接解决问题的。
不要忽略它(译者注:Lodash),但——下次你需求一个抽象时——思考一下是否一个简单的函数就能办到了!

2 关注 分享
1***@qq.com HRK_01

要回复文章请先登录注册