2. 内窥underscore源码实现链式调用

52 阅读2分钟

本文概览:

  1. 通过添加sort来重构相同逻辑部分
  2. 查看 underscore 链式调用源码实现

新的需求: 如果我们添加map函数 紧接着上一篇,如果我们需要添加sort怎么办?那么容易

_.prototype.sort = function () {
  const obj = this._wrapped
  Array.prototype.sort.apply(obj, arguments)
  return chainResult(this, obj)
}

很简单的就实现了上述的需求,但是如果我们现在在看一下我们的实现:

_.prototype.reverse = function () {
  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)
}
_.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)
}
_.prototype.sort = function () {
  const obj = this._wrapped
  Array.prototype.sort.apply(obj, arguments)
  return chainResult(this, obj)
}
_.prototype.value = function () {
  return this._wrapped

}

上面所有实例上的方法,包括reverse, push, pop, shift, unshift, srot其实都是相似的,所以我们想到了提取出去,提取成一个叫做handleArrayMethods 的方法 所以将上述实例上的数组的操作方法提取一下

function handleArrayMethods() {
  const methods = ['reverse', 'push', 'pop', 'shift', 'unshift', 'sort']
  const ArrayMethod = Array.prototype
  methods.forEach(method => {
    _.prototype[method] = function () {
      const obj = this._wrapped
      ArrayMethod[method].apply(obj, arguments)
      return chainResult(this, obj)
    }
  })
}
handleArrayMethods()

然后测试一下之前的case, okok, 跑的通,没问题

我们看一下underscore怎么实现的链式调用?

// underscore-array-methods.js
import _ from './underscore.js';
import each from './each.js';
import { ArrayProto } from './_setup.js';
import chainResult from './_chainResult.js';

// Add all mutator `Array` functions to the wrapper.
// 看这个each 函数,就是和我们实现的 handleArrayMethods 差不多的功能
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  var method = ArrayProto[name];
  _.prototype[name] = function() {
    var obj = this._wrapped;
    if (obj != null) {
      method.apply(obj, arguments);
      // 这一段为什么实现?暂时不知道啊哎
      if ((name === 'shift' || name === 'splice') && obj.length === 0) {
        delete obj[0];
      }
    }
    return chainResult(this, obj);
  };
});

// Add all accessor `Array` functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
  var method = ArrayProto[name];
  _.prototype[name] = function() {
    var obj = this._wrapped;
    if (obj != null) obj = method.apply(obj, arguments);
    return chainResult(this, obj);
  };
});

export default _;

我们看一下_构造函数实现

// 获取 version 版本
import { VERSION } from './_setup.js';

// 声明 _ 构造函数,好像和我们实现的差不多

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

_.VERSION = VERSION;

// 添加 value 来获取 _wrapped 的值
_.prototype.value = function() {
  return this._wrapped;
};

// Provide unwrapping proxies for some methods used in engine operations
// such as arithmetic and JSON stringification.
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

_.prototype.toString = function() {
  return String(this._wrapped);
};

在看一下chainResult的实现

// _chainResult.js
import _ from './underscore.js';

// Helper function to continue chaining intermediate results.
export default function chainResult(instance, obj) {
  // 为啥_chain是true的时候又给obj包了一下,直接返回instance 不行吗?
  return instance._chain ? _(obj).chain() : obj;
}