通过 underscore 的链式调用带来的思考
_([1, 2, 3]).reverse() // [3, 2, 1]
const res = _.chain([4, 5, 6]).push(7, 8, 9).reverse().pop().value() // [9, 8, 7, 6, 5, 4]
console.log(res)
1. _() 和 _.chain() 的比较得出什么?
_()我们可以看成是_函数,执行后的结果, 那么带有一个叫做reverse的方法js function _ () { return { reverse() {} } }_.chain()表示函数_上面有个属性叫做chain, 并且chain是一个函数, 因为函数是一个一等公民,所以我们可以在_上添加属性js function _() { return { reverse() {} } } _.chain = function () {}- 但是
chian之后又有push, 那么这样才合理function _() { return { reverse() {} } } _.chain = function () { return { push() {} } } - 所谓链式调用,是方法后面接方法,
chain后边可能加上push方法,也有可能在chain加上reverse..., 那么这样才合理那么这样下去,就是一个无穷的死循环, 所以我们换个思路思考. 如果我们把function _() { return { reverse() {} } } _.chain = function () { return { push() { return { reverse () {}, slice() {} } }, reverse() {}, slice() {} } }push,reverse,slice都作为实例上的方法,并且在每个方法执行之后,就把当前的实例返回
2. 换个姿势再来
-
将
_当成构造函数function _(obj) { // 如果 obj 是 _ 的实例,则直接返回 if (obj instanceof _) return obj // 保证如果用函数调用的方式,返回的依然是 _ 的实例 if (!(this instanceof _)) return new _(obj) // 内置属性来存储 this._wrapped = obj } _.prototype.reverse = function () {} _.chain = function () {} -
把
reverse方法实现一下_.prototype.reverse = function () { return this._wrapped.reverse() } -
把
value方法实现一下_.prototype.value = function() { return this._wrapped } -
跑一下代码, 发现例子1通过, 可是 2 该怎么搞呢?
3. 实现例子2
_.chain也是应该返回_的实例_.chain = function (obj) { return _(obj) }- 这样下来发现
_.chain和_()创建的没什么区别,但是_.chain()创建出来的支持链式调用?这该怎么搞? 加一个_chain的标志位来进行区分吧_.chain = function (obj) { const instance = _(obj) instance._chain = true return instance } - 那么我们先将
push,slice的占槽搞定_.prototype.push = function () {} _.prototype.pop = function () {} - 实现
reverse_.prototype.reverse = function () { // 需要对 _wrapped 进行reverse 处理 const obj = this._wrapped Array.prototype.reverse.apply(obj, arguments) // 将当前实例返回 return this } - 跑一下
const res = _.chain([4, 5, 6]).push(7, 8, 9).reverse().pop().value() console.log(res) // [6, 5, 4] - 发现一切尽在掌握之中
case 1中的返回值不对了console.log(_([1, 2, 3]).reverse()) // 返回了 _ 的实例
4. 在 reverse, push, pop 的返回值需要区分是不是被chain包裹的
-
那么我们的标志位
_chain就要起作用了function chainResult(instance, obj) { return instance._chain ? instance : obj } _.prototype.reverse = function () { // 需要对 _wrapped 进行reverse 处理 const obj = this._wrapped Array.prototype.reverse.apply(obj, arguments) return chainResult(this, obj) } -
同理
push和pop也需要这样的操作function chainResult(instance, obj) { return instance._chain ? instance : obj } _.prototype.reverse = function () { // 需要对 _wrapped 进行reverse 处理 const obj = this._wrapped Array.prototype.reverse.apply(obj, arguments) return chainResult(this, obj) } _.prototype.push = function () { const obj = this._wrapped Array.prototype.push.apply(obj, arguments) return chainResult(this, obj) } _.prototype.pop = function () { const obj = this._wrapped Array.prototype.pop.apply(obj, arguments) return chainResult(this, obj) } -
跑一下
case 2, 木问题啦 -
如果我们添加一个
shift和unshift函数呢 -
我们直接按照
reverse模板进行_.prototype.shift = function () { const obj = this._wrapped Array.prototype.shift.apply(obj, arguments) return chainResult(this, obj) } _.prototype.unshift = function () { const obj = this._wrapped Array.prototype.unshift.apply(obj, arguments) return chainResult(this, obj) } -
测试下面例子
const res = _.chain([4, 5, 6]).push(7, 8, 9).reverse().pop().shift().unshift(10, 11, 12) console.log(res.value()) -
也木问题啦
6. 总结
- 如果遇到链式调用,疯狂往
OOP上靠 - 所有的操作(
reverse,push,pop,shift,unshift)都要在当前实例上 - 在实例中搞出来一个属性(
_wrapped)用来记录每次操作的结果值
7. 存在的问题
reverse,push,pop,shift,unshift的写法重复很多,需要提取出来- 看看
underscore的实现