这是 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 链式调用的实现方法
基本思路和上面一样:
- 提供一个 chain 方法,该方法返回 underscore 实例
- 在 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 很巧妙的实现了链式调用,就本人目前的水平只能说看懂代码,如果之前没接触过这种代码估计永远也想不出来,所以要学的还有很多。