Lodash用來操作對象和集合,比Underscore擁有更多的功能和更好的性能。
官網:https://lodash.com/
引用:<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
安裝:npm install lodash
首先通過npm安裝lodash:
npm i --save lodash
在js文件中引用lodash:
var _ = require('lodash');
本系列包括:
● lodash用法系列(1),數組集合操作
● lodash用法系列(2),處理對象
● lodash用法系列(3),使用函數
● lodash用法系列(4),使用Map/Reduce轉換
● lodash用法系列(5),鏈式
● lodash用法系列(6),函數種種
■ 不同的函數定義方式
var collection = [ { name: 'Ronnie', age: 43 }, { name: 'Ben', age: 19 }, { name: 'Sharon', age: 25 }, { name: 'Melissa', age: 29 } ]; //返回name鍵值組成的數組 function collectionNames(){ //依賴於變量 //name是hardcode return _.map(collection, 'name'); } //coll形參,輸入集合 //prop形參,輸入集合對象的某個字段 //map中的參數完全依賴形參,coll, prop泛型 function inderectionNames(coll, prop){ return _.map(coll, prop); } //coll形參,輸入集合 //coll泛型 function genericCollNames(coll){ return _.map(coll, 'name'); } //prop形參,輸入集合對象的某個字段 //prop泛型 function genericPropNames(prop){ return _.map(collection, prop); } //[ 'Ronnie', 'Ben', 'Sharon', 'Melissa' ] console.log(collectionNames()); //[ 'Ronnie', 'Ben', 'Sharon', 'Melissa' ] console.log(inderectionNames(collection, 'name')); //[ 'Ronnie', 'Ben', 'Sharon', 'Melissa' ] console.log(genericCollNames(collection)); //[ 'Ronnie', 'Ben', 'Sharon', 'Melissa' ] console.log(genericPropNames('name'));
■ 函數的參數都不是靜態的、固定的,但可以到arguments中提取
function insert(coll, callback){ var toInsert; //也就是callback這個參數是optional的 if(_.isFunction(callback)){ toInsert= _.slice(arguments,2);//截掉insert參數中coll, callback這2個實參 }else{ toInsert= _.slice(arguments,1) callback= _.identity;//_.identity是lodash回調函數的默認值 } _.each(toInsert, function(item){ //把加入的元素放到coll集合中合適的位置 coll.splice(_.sortedIndex(coll, item, callback),0, item); }); return coll; } var collection=_.range(1,11); var result = insert(collection,8.4); //[ 1, 2, 3, 4, 5, 6, 7, 8, 8.4, 9, 10 ] console.log(result);
■ 函數的參數不是靜態的、固定的,輸入部分實參
以上在調用函數的時候是把所有的實參給了函數,而在lodash中,還可以分步輸入實參。
//接受兩個形參 var greet = function (greeting, name) { return greeting + ' ' + name; } //先輸入第一個實參 var sayHello = _.partial(greet, 'hello'); //再輸入第二個實參 //hello darren console.log(sayHello('darren')); //輸入兩個實參,第一個用占位符 var greetJack = _.partial(greet, _, 'jack'); //hi jack console.log(greetJack('hi'));
以上,greet函數接受2個形參,但我們在實際使用過程中,先輸入一個實參,再輸入其它實參。
如果先輸入第二個實參,就使用partialRight方法。如果使用partialRight而不輸入第二個實參,只輸入第一個實參,那第二個實參需要用占位符。
與parital類似的還有一個方法使curry:
var add = function (a, b) { return a + b; } var c1 = _.curry(add); var c2 = c1(5); var result = c2(6); //11 console.log(result);
partial和curry的共同點都是需要把實參湊齊了才能執行方法。不同點是:parital似乎把2個參數交給了一個人,一個人先后拿到所有的實參;curry似乎是把不同的參數交給了不同的人,拿2個實參為例,拿到第一個實參交給一個人c1,c1說我拿了實參5告訴c2,c2知道后有拿了參數6,2個實參拿齊后,由c2執行方法。
■ 自定義的函數作為回調函數
var YEAR_MILLISECONDS = 31560000000; function validItem(item){ return item.age > 21 && _.isString(item.first) && _.isString(item.last); } var invalidItem = _.negate(validItem); function computed(item) { return _.extend({ name: _.result(item, 'first', '') + ' ' + _.result(item, 'last', ''), yob: new Date(new Date() - (YEAR_MILLISECONDS * item.age)) .getFullYear() }, item); } var collection = [ { first: 'Roderick', last: 'Campbell', age: 56 }, { first: 'Monica', last: 'Salazar', age: 38 }, { first: 'Ross', last: 'Andrews', age: 45 }, { first: 'Martha', age: 51 } ]; console.log(_.every(collection, validItem));//false //[ { first: 'Roderick', last: 'Campbell', age: 56 }, // { first: 'Monica', last: 'Salazar', age: 38 }, // { first: 'Ross', last: 'Andrews', age: 45 } ] console.log(_.filter(collection, validItem)); //{ first: 'Martha', age: 51 } console.log(_.find(collection, invalidItem)); //[ { name: 'Roderick Campbell', // yob: 1959, // first: 'Roderick', // last: 'Campbell', // age: 56 },...] console.log(_.map(collection, computed));
■ 把過濾封裝到一個函數中
function byName(coll, name, take) { return _(coll) .filter({ name: name }) .take(_.isUndefined(take) ? 100 : take) .value(); } var collection = [ { name: 'Theodore', enabled: true }, { name: 'Leslie', enabled: true }, { name: 'Justin', enabled: false }, { name: 'Leslie', enabled: false } ]; byName(collection, 'Leslie'); byName(_.filter(collection, 'enabled'), 'Leslie'); byName(_(collection).filter('enabled'), 'Leslie');
以上,把過濾封裝到了byName方法中,並且在該方法內返回值,即直接調用了方法。
■ 把鏈式封裝到一個函數中
function sort(coll, prop, desc){ var wrapper = _(coll).sortBy(prop); return desc? wrapper.reverse() : wrapper; } var collection = [ { first: 'Bobby', last: 'Pope' }, { first: 'Debbie', last: 'Reid' }, { first: 'Julian', last: 'Garcia' }, { first: 'Jody', last: 'Greer' } ]; var result = sort(collection,'first').value(); //[ { first: 'Bobby', last: 'Pope' }, // { first: 'Debbie', last: 'Reid' }, // { first: 'Jody', last: 'Greer' }, // { first: 'Julian', last: 'Garcia' } ] console.log(result); var result2=sort(collection,'last') .takeRight(2) .pluck('last') //獲取某個字段的值放在數組中 .value(); //[ 'Pope', 'Reid' ] console.log(result2);
以上,把鏈式封裝到了sort方法中,但在該方法內沒有返回值,該方法可以看做是鏈式的一個wrapper。
■ 補充:indexBy的用法
var keyData = [ { 'dir': 'left', 'code': 97 }, { 'dir': 'right', 'code': 100 } ]; var result1= _.indexBy(keyData,'dir'); //把dir字段對應的鍵值作為鍵,把集合元素作為鍵值 //{ left: { dir: 'left', code: 97 }, // right: { dir: 'right', code: 100 } } console.log(result1);
■ 把幾個方法合成起來
function enabledIndex(obj){ return _.transform(obj, function(result, value, key){ result[key]= _.result(value, 'enabled',false); }); } var collection = [ { name: 'Claire', enabled: true }, { name: 'Patricia', enabled: false }, { name: 'Mario', enabled: true }, { name: 'Jerome', enabled: false } ]; //實際上indexByName接受2個形參,一個是集合,一個是字段 //indexByName的返回結果是以name屬性值為key,集合元素作為value //{ Claire: { name: 'Claire', enabled: true }, // Patricia: { name: 'Patricia', enabled: false }, // Mario: { name: 'Mario', enabled: true }, // Jerome: { name: 'Jerome', enabled: false } } var indexByName=_.partialRight(_.indexBy, 'name'), //把第一個參數collection傳給第一個方法IndexByName //第一個方法的返回值作為第二個方法的實參 enabled=_.partial(_.flow(indexByName, enabledIndex), collection); //{ Claire: true, Patricia: false, Mario: true, Jerome: false } console.log(enabled());
以上,通過flow方法把indexByName和enabledIndex方法合成了起來,並且,第一個方法的返回值作為第二個方法的實參。
■ 合成函數,並作為回調函數
var collection = [ { first: 'Andrea', last: 'Stewart', age: 28 }, { first: 'Clarence', last: 'Johnston', age: 31 }, { first: 'Derek', last: 'Lynch', age: 37 }, { first: 'Susan', last: 'Rodgers', age: 41 } ]; var minimal=_.flow(_.identity, _.partialRight(_.pick, ['last','age'])); var result=_.map(collection, minimal); //[ { last: 'Stewart', age: 28 }, // { last: 'Johnston', age: 31 }, // { last: 'Lynch', age: 37 }, // { last: 'Rodgers', age: 41 } ] console.log(result);
■ 合成函數,控制鏈式的過程
function sorted(wrapper){ return _(wrapper).sortBy(); } function rejectOdd(wrapper){ return _(wrapper).reject(function(item){ return item%2; }); } var sortedEvens=_.flow(sorted, rejectOdd), evensSorted=_.flow(rejectOdd, sorted, _.partialRight(_.result, 'value')), collection=_.shuffle(_.range(1,11)); var result=sortedEvens(collection) .reverse() .value(); //[ 10, 8, 6, 4, 2 ] console.log(result);
以上,sorted和sortedEvens方法接收的是wrapper,正是因為是wrapper,所以在flow中可以改變這些wrapper的順序,即控制鏈式過程。
■ 在函數的方法中顯示使用鏈式
function validThru(next, value){ return value && next; } function User(first, last, age){ this.first=first; this.last=last; this.age=age; } User.prototype.valid = function(){ return _.chain(this.first) //一旦顯式調用chain方法,意味着接下來那些返回值的函數返回wrapper .isString() .thru(_.partial(validThru, this.last))//validThru相當於thru的回調函數 .isString() .thru(_.partial(validThru, this.age)) .isFinite() .value(); } var result = new User('orlando','Olson',25).valid(); console.log(result);
■ 自定義內置方法average
//自定義average方法 _.mixin({average:function(coll, callback){ return _(coll) .map(callback) .reduce(function(result, item){ return result +item; }) / _.size(coll); }}); var collection = [ { name: 'Frederick', age: 41, enabled: true }, { name: 'Jasmine', age: 29, enabled: true }, { name: 'Virgil', age: 47, enabled: true }, { name: 'Lila', age: 22, enabled: false } ]; var result = _.average(collection, 'age'); //34.75 console.log(result);
參考資料:lodash essentials
本系列結束☺