可以说是 lodash 中最为重要的部件,想要用lodash进行复杂的多步操作都离不开chain的帮助。
首先说说Chain的调用方式有两种:一种是显式调用(Explicit Chaining),一种是隐式调用(Implicit Chaining)。
//下面的例子采用了ECMAScript2015的语法:
//显式调用例子如下:
let numbers = [1, 2, 3, 4, 5];
let sumOfEvenSquares = _.chain(numbers) //注意numbers放置的位置
.filter(n => n % 2 === 0)
.map(n => n * n)
.sum()
.value();
//sumOfEvenSquares: 20
//特别要注意结尾的那个.value()
//隐式调用例子如下:
let sumOfEvenSquares = _(numbers) //注意numbers的位置,不需要显式的使用chain关键字
.filter(n => n % 2 === 0)
.map(n => n * n)
.sum()
let isEven = n => n % 2 === 0;
let square = n => n * n;
let sumOfEvenSquares = _(numbers).filter(isEven).map(square).sum();
从上面例子可以看出,隐式调用比显式调用更加简洁方便。
为什么要多出来一个 .value(),而不是直接出结果呢?那是因为可能要等待延时(Deferred execution)数据的到来,再执行取值。这就是我们常说的Lazy evaluation (延迟计算/惰性求值)
//如下面的例子:首先生成链式表达式
var wallet = _(assets).filter(ownedBy('me'))
.pluck('value')
.reduce(sum);
$json.get("/new/assets").success(function(data) {
assets.push.apply(assets, data); // 然后更新数据
wallet.value(); // 最后求值
});
隐式
_(value)建立了一个隐式链对象,可以把那些能操作并返回 arrays(数组)、collections(集合)、functions(函数)的”.Methods”(lodash的函数)串起来。 那些能返回“唯一值(single value)”或者可能返回原生数据类型(primitive value)会自动结束链式反应。
显式
而显式链则用_.chain. 的方式实现延迟计算,即:求值操作等到 _value()被调用时再执行。
延迟计算允许一些方法支持shortcut fusion 。由于执行被延后了,因此lodash可以进行shortcut fusion这样的优化,通过合并链式iteratee大大降低迭代的次数。
The wrapper Array methods are(支持wrapper的Array方法有):
concat, join, pop, push, reverse,shift, slice, sort, splice, unshift
The wrapper String methods are(支持wrapper的String方法有):
replace,split
The wrapper methods that support shortcut fusion are(支持shortcut fusion的方法有):
compact, drop, dropRight, dropRightWhile, dropWhile, filter, first, initial, last,map, pluck, reject, rest, reverse, slice, take, takeRight, takeRightWhile, takeWhile, toArray, where
he chainable wrapper methods are(可以链式反应的wrapper方法有):
after, ary, assign, at, before, bind, bindAll, bindKey, callback, chain, chunk, commit, compact,concat, constant, countBy, create, curry, debounce, defaults,defaultsDeep, defer, delay, difference, drop, dropRight, dropRightWhile, dropWhile, fill, filter, flatten, flattenDeep, flow, flowRight, forEach, forEachRight, forIn, forInRight, forOwn, forOwnRight, functions, groupBy, indexBy, initial, intersection,
invert, invoke, keys, keysIn, map, mapKeys, mapValues, matches,
matchesProperty, memoize, merge, method, methodOf, mixin, modArgs, negate, omit, once, pairs, partial, partialRight, partition, pick,
plant, pluck, property, propertyOf, pull, pullAt, push, range, rearg,
reject, remove, rest, restParam, reverse, set, shuffle, slice, sort,
sortBy, sortByAll, sortByOrder, splice, spread, take, takeRight,
takeRightWhile, takeWhile, tap, throttle, thru, times, toArray,
toPlainObject, transform, union, uniq, unshift, unzip, unzipWith,
values, valuesIn, where, without, wrap, xor, zip, zipObject, zipWith
The wrapper methods that are not chainable by default are(缺省情况下不能加在”链条”中间的wrapper方法有):
add, attempt, camelCase, capitalize, ceil, clone, cloneDeep, deburr, endsWith, escape,escapeRegExp, every, find, findIndex, findKey, findLast,findLastIndex, findLastKey, findWhere, first, floor, get, gt, gte, has, identity, includes, indexOf, inRange, isArguments, isArray,isBoolean, isDate, isElement, isEmpty, isEqual, isError, isFinite, isFunction, isMatch, isNative, isNaN, isNull, isNumber, isObject, isPlainObject, isRegExp, isString, isUndefined, isTypedArray, join, kebabCase, last, lastIndexOf, lt, lte, max, min, noConflict, noop, now, pad, padLeft, padRight, parseInt, pop, random, reduce, reduceRight, repeat, result, round, runInContext, shift, size, snakeCase, some, sortedIndex, sortedLastIndex, startCase, startsWith, sum, template, trim, trimLeft, trimRight, trunc, unescape, uniqueId, value, words
//举个例子:
var wrapped = _([1, 2, 3]);
// 隐式链中,reduce是属于不能直接加在"链条中间"(not chainable)的,所以能立即执行计算
wrapped.reduce(function(total, n) {
return total + n;
});
// → 6
// 而map是chainable的
var squares = wrapped.map(function(n) {
return n * n;
});
//所以不调用value()时返回的并不是一个Array而是一个wrapper
_.isArray(squares);
// → false
//调用了value后返回的就是一个wrapper了
_.isArray(squares.value());
// → true
拦截器,拿tap来干嘛?你可以在链条当中插它一下,可以对数据进行处理,可以返回值也可以不返回值,也可以仅仅是打印一下中间的过程,拿tap来debug 链式反应的过程是不错的选择。
_([1, 4, 3, 5])
.tap(function(array) {
array.pop(); //对array进行操作
console.log(array); //打印
return array.reverse(); //返回值,也可以不返回值
})
.value(); //取值
// → [3,4,1]
和 tap非常相似,但是必须有返回值,否则会报错!
_(' abc ')
.chain()
.trim()
.thru(function(value) {
return '处理后:[' + value + ']'; //必须有返回值
})
.value();
// → 处理后:[abc]
允许wrapper对象使用显式链。这样就允许隐式链可以切换为显式链。
var users = [
{ 'user': '阿帕奇', 'age': 36 },
{ 'user': '弗雷德', 'age': 40 }
];
//第一个例子没用显式链,注意first()在缺省情况下是只能被加在反应链尾部的
//参看前面的 ***not chainable***
_(users).first();
// → { 'user': '阿帕奇', 'age': 36 }
// 第二个例子启用了显式链,在启用了chain后,first就可以加到链条中间了
_(users).chain()
.first()
.pick('user')
.value();
// → { 'user': '阿帕奇' }
执行链条并返回wrapped instance;
要注意与.prototype.value()的区别,commit()与value()都会触发链式反应,但是返回的值不同
var array = [1, 2];
var wrapped = _(array).push(3);
console.log(array);
// → [1, 2] 这个时候 array的值还没有发生变化
wrapped = wrapped.commit();
console.log(wrapped);
//{ __wrapped__: [ 1, 2, 3 ], __actions__: [], __chain__: false }
console.log(array);
// → [1, 2, 3] 在commit之后 array的值发生变化了
wrapped.last();
// → 3
执行链条,并且返回unwrapped后的值,注意与上边.commit()的比较
_([1, 2, 3]).value();
// → [1, 2, 3]
新建一个数组,将原始数组与新加入的值合并起来。
var array = [1];
var wrapped = _(array).concat(2, [3], [[4]]);
console.log(wrapped.value());
// → [1, 2, 3, [4]]
console.log(array);
// → [1] 注意:原数组并未发生改变
克隆一个已有的操作链
var array = [1, 2];
var wrapped = _(array).map(function(value) {
return Math.pow(value, 2);
});
var other = [3, 4];
var otherWrapped = wrapped.plant(other);
otherWrapped.value();
// → [9, 16]
wrapped.value();
// → [1, 4]
反转数组元素的次序:注意改方法会改变被操作的数组。
var array = [1, 2, 3];
_(array).reverse().value()
// → [3, 2, 1]
console.log(array);
// → [3, 2, 1]
将unwrapped 的值转为字符串。
_([1, 2, 3]).toString();
// → '1,2,3'