【underscore】实现链式调用

这是 underscore 学习的第二章,本章我们在上一章的基础上实现链式调用功能的实现。具体思路参考:https://github.com/mqyqingfeng/Blog/issues/57

首先 underscore 默认并没有提供链式调用,需要使用 _.chain(obj) 作为调用第一段的方法,让后面的方法支持链式调用。

链式调用的核心是返回 underscore 的实例对象,先写个小 demo 演示这一点

function Jq(obj) {
  this.obj = obj
  this.push = function(item) {
    this.obj.push(item)
    return this
  }
  this.reverse = function() {
    this.obj.reverse()
    return this
  }
}

const jq = new Jq([1, 2, 3])
jq.push(1).push(2).reverse() // Jq {obj: Array(5), push: ƒ, reverse: ƒ}

可以看到虽然实现了链式调用但是因为没有显式指定调用的终点所以返回的还是 this,一个解决方法是提供一个显式的.value()方法获取对象的结果。

underscore 链式调用的实现方法
基本思路和上面一样:

  1. 提供一个 chain 方法,该方法返回 underscore 实例
  2. 在 mixin 中判断是否为 chain 调用
_.chain = function(obj) {
  // 创建 underscore 实例
  // instance = {_wrapped: obj}
  const instance = _(obj)
  // 指示为链式调用
  instance._chain = true
  return instance
}

// 注意这里 value 只能挂在 prototype 上
// 否则 this 为 _ 
_.prototype.value = function() {
  return this._wrapped
}

_.mixin = function(obj) {
  for(const name of _.functions(obj)) {
    const func = _[name] = obj[name]
    _.prototype[name] = function(...args) {
      const arg = this._wrapped
      // 如果是链式调用,将函数的结果当成 chain 的参数继续传递,实现链式调用
      const rst = func.call(_, arg, ...args)
      if(this._chain) {
        return _.chain(rst)
      } else {
        return rst
      }
    }
  }
}

_.mixin(_)

// 同样链式调用会返回 this,所以需要一个方法显式返回结果
_.chain('text').reverse().reverse().value() // text

完整代码

(function () {
    // 只考虑最简单的情况:非严格模式且this存在
    const root = this;

    const _ = function (obj) {
        if (obj instanceof _) { return obj }
        if (!(this instanceof _)) return new _(obj)
        this._wrapped = obj
    }

    root._ = _

    _.log = function () {
        console.log(this)
    }

    _.each = function (obj, cb) {
        if (obj instanceof Array) {
            for (let i = 0; i < obj.length; i++) {
                if (cb.call(obj, obj[i], i) === false) {
                    break
                }
            }
        } else {
            for (const key in obj) {
                if (cb.call(obj, obj[key], key) === false) {
                    break
                }
            }
        }

        return obj
    }

    _.functions = function (obj) {
        const names = []
        for (const key in obj) {
            if (typeof obj[key] === 'function') {
                names.push(key)
            }
        }

        return names
    }

    // 实现链式调用
    _.chain = function (obj) {
        const instance = _(obj)
        instance._chain = true
        return instance
    }

    _.prototype.value = function () {
        return this._wrapped
    }

    _.reverse = function (string) {
        return string.split('').reverse().join('')
    }

    // 挂载对象方法到 prototype
    _.mixin = function (obj) {
        _.each(_.functions(obj), (name) => {
            const func = _[name] = obj[name]
            _.prototype[name] = function (...args) {
                const arg = this._wrapped
                return this._chain ? _.chain(func.call(_, arg, ...args)) : func.call(_, arg, ...args)
            }
        })
    }

    _.mixin({
        ..._,
        addOne(num) {
            return num + 1
        }
    })
})()

总结
可以看出 underscore 很巧妙的实现了链式调用,就本人目前的水平只能说看懂代码,如果之前没接触过这种代码估计永远也想不出来,所以要学的还有很多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值