【underscore】函数式调用和面向对象式调用方法的实现

函数式调用
_.reverse(‘text’)

面向对象式调用
_(‘text’).reverse()

可以看到二者的区别是函数式调用是直接使用underscore调用方法,而面向对象调用的方法是先创建underscore对象,然后调用方法(不传参)

从上面可以看出,underscore 对象不是一个简单的对象,除了能调用方法外,还可以调用自身,在 JS 中函数也是一种对象,且符合我们的要求。

underscore 构造函数

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

上面的代码,调用时先判断调用者是否是 underscore 的实例,如果不是就以 obj 为参数创建 underscore 实例,该实例包含一个 _wrapped 属性用来保存 obj。

例子:

_('text')
// 调用过程分为如下几步:
// 1. 因为 this === window(假设在浏览器环境),不是 _ 实例,所以创建 _ 实例
// 2. new _(obj) 再次调用 _,此时 this 是 _ 的实例,所以运行 this._wrapped = obj

挂载静态方法到 underscore

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

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

// 调用
_.reverse('hello') // olleh

把静态方法挂到 prototype 上
前面用 _('text').reverse() 的方法调用,实际是创建一个 underscore 实例,然后调用这个实例的 reverse 方法,显然我们需要把方法挂到 prototype 上,而不是给这个实例添加方法。

具体步骤分为如下几步:

  1. 找到所有静态方法
  2. 遍历静态方法并包装执行函数
// ... 代码同上
// 找到 obj 的所有静态方法并返回方法名数组
_.functions = function(obj) {
  const names = []
  for(const key in obj) {
    if(obj[key] instanceof Function) {
      names.push(key)
    }
  }
  
  return names
}

// 把实例方法挂载到 prototype
_.mixin = function(obj) {
  for(const name of _.functions(obj)) {
    // 注意这行的 _[name] = obj[name]
    // 有了 _[name] = obj[name],我们可以扩展 _ 的方法
    const fn = _[name] = obj[name]
    obj.prototype[name] = function(...args) {
      const arg = this._wrapped // 获取创建对象时传入的参数
      return fn.call(_, arg, ...args)
    }
  }
}

_.mixin(_)

完整代码

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

    const _ = function (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
    }

    _.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 func.call(_, arg, ...args)
            }
        })
    }

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

参考资料
https://github.com/mqyqingfeng/Blog/issues/56

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值