Underscore 团体架构浅析

媒介

终究,楼主的「Underscore 源码解读系列」underscore-analysis 行将进入尾声,关注下 timeline 会发现楼主近来加速了解读速率。十一月,多事之秋,近来许多事变搞的楼主心力枯槁,身心俱疲,也想尽快把这个系列完结掉,也好了结一件苦衷。

本文估计是解读系列的倒数第二篇,末了一篇那末明显就是大总结了。楼主的 Underscore 系列解读完整版地点 https://github.com/hanzichi/u…

通例挪用

之前写的文章,关注点大多在详细的要领,详细的学问细节,也有读者留言发起楼主讲讲团体架构,这是必需会讲的,只是楼主把它安排在了末了,也就是本文,由于楼主觉得不控制团体架构关于详细要领的明白也是没有大的题目的。

Underscore 大多数时刻的挪用情势为 _.funcName(xx, xx),这也是 文档中 的挪用体式格局。

_.each([1, 2, 3], alert);

最简朴的完成体式格局,我们能够把 _ 看作一个简朴的对象:

var _ = {};
_.each = function() {
  // ...
};

在 JavaScript 中,统统皆对象,实际上,源码中的 _ 变量是一个要领:

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

为何会是要领?我们接下去看。

OOP

Underscore 支撑 OOP 情势的挪用:

_([1, 2, 3]).each(alert);

这实际上是异常典范的「无 new 组织」,_ 实在就是一个 组织函数_([1, 2, 3]) 的结果就是一个对象实例,该实例有个 _wrapped 属性,属性值是 [1, 2, 3]。实例要挪用 each 要领,其自身没有这个要领,那末应当来自原型链,也就是说 _.prototype 上应当有这个要领,那末,要领是怎样挂载上去的呢?

要领挂载

如今我们已明白以下两点:

  1. _ 是一个函数(支撑无 new 挪用的组织函数)

  2. _ 的属性有许多要领,比方 _.each_.template 等等

我们的目的是让 _ 的组织实例也能挪用这些要领。细致想一想,实在也不难,我们能够遍历 _ 上的属性,假如属性值范例是函数,那末就将函数挂到 _ 的原型链上去。

源码中用来完成这件事的是 _.mixin 要领:

// Add your own custom functions to the Underscore object.
// 可向 underscore 函数库扩大本身的要领
// obj 参数必需是一个对象(JavaScript 中统统皆对象)
// 且本身的要领定义在 obj 的属性上
// 如 obj.myFunc = function() {...}
// 形如 {myFunc: function(){}}
// 以后便可运用以下: _.myFunc(..) 或许 OOP _(..).myFunc(..)
_.mixin = function(obj) {
  // 遍历 obj 的 key,将要领挂载到 Underscore 上
  // 实际上是将要领浅拷贝到 _.prototype 上
  _.each(_.functions(obj), function(name) {
    // 直接把要领挂载到 _[name] 上
    // 挪用相似 _.myFunc([1, 2, 3], ..)
    var func = _[name] = obj[name];

    // 浅拷贝
    // 将 name 要领挂载到 _ 对象的原型链上,使之能 OOP 挪用
    _.prototype[name] = function() {
      // 第一个参数
      var args = [this._wrapped];

      // arguments 为 name 要领须要的其他参数
      push.apply(args, arguments);
      // 实行 func 要领
      // 支撑链式操纵
      return result(this, func.apply(_, args));
    };
  });
};

// Add all of the Underscore functions to the wrapper object.
// 将前面定义的 underscore 要领增加给包装过的对象
// 即增加到 _.prototype 中
// 使 underscore 支撑面向对象情势的挪用
_.mixin(_);

_.mixin 要领能够向 Underscore 库增添本身定义的要领:

_.mixin({
  capitalize: function(string) {
    return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
  }
});
_("fabio").capitalize();
=> "Fabio"

同时,Underscore 也加入了一些 Array 原生的要领:

// Add all mutator Array functions to the wrapper.
// 将 Array 原型链上有的要领都增加到 underscore 中
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  var method = ArrayProto[name];
  _.prototype[name] = function() {
    var obj = this._wrapped;
    method.apply(obj, arguments);

    if ((name === 'shift' || name === 'splice') && obj.length === 0)
      delete obj[0];

    // 支撑链式操纵
    return result(this, obj);
  };
});

// Add all accessor Array functions to the wrapper.
// 增加 concat、join、slice 等数组原生要领给 Underscore
_.each(['concat', 'join', 'slice'], function(name) {
  var method = ArrayProto[name];
  _.prototype[name] = function() {
    return result(this, method.apply(this._wrapped, arguments));
  };
});

链式挪用

Underscore 也支撑链式挪用:

// 非 OOP 链式挪用
_.chain([1, 2, 3])
  .map(function(a) {return a * 2;})
  .reverse()
  .value(); // [6, 4, 2]

// OOP 链式挪用
_([1, 2, 3])
  .chain()
  .map(function(a){return a * 2;})
  .first()
  .value(); // 2

乍一看好像有 OOP 和非 OOP 两种链式挪用情势,实在只是一种,_.chain([1, 2, 3])_([1, 2, 3]).chain() 的结果是一样的。怎样完成的?我们深切 chain 要领看下。

_.chain = function(obj) {
  // 不管是不是 OOP 挪用,都邑转为 OOP 情势
  // 而且给新的组织对象增加了一个 _chain 属性
  var instance = _(obj);

  // 标记是不是运用链式操纵
  instance._chain = true;

  // 返回 OOP 对象
  // 能够看到该 instance 对象除了多了个 _chain 属性
  // 其他的和直接 _(obj) 的结果一样
  return instance;
};

我们看下 _.chain([1, 2, 3]) 的结果,将参数代入函数中,实在就是对参数举行无 new 组织,然后返回实例,只是实例多了个 _chain 属性,其他的和直接 _([1, 2, 3]) 如出一辙。再来看 _([1, 2, 3]).chain()_([1, 2, 3]) 返回组织实例,该实例有 chain 要领,挪用要领,为实例增加 _chain 属性,返回该实例对象。所以,这两者结果是一致的,结果都是转为了 OOP 的情势。

说了这么多,好像还没讲到正题上,它是怎样「链」下去的?我们以以下代码为例:

_([1, 2, 3])
  .chain()
  .map(function(a){return a * 2;})
  .first()
  .value(); // 2

当挪用 map 要领的时刻,实际上能够会有返回值。我们看下 _.mixin 源码:

// 实行 func 要领
// 支撑链式操纵
return result(this, func.apply(_, args));

result 是一个主要的内部协助函数(Helper function ):

// Helper function to continue chaining intermediate results.
// 一个协助要领(Helper function)
var result = function(instance, obj) {
  // 假如须要链式操纵,则对 obj 运转 chain 要领,使得能够继承后续的链式操纵
  // 假如不须要,直接返回 obj
  return instance._chain ? _(obj).chain() : obj;
};

假如须要链式操纵(实例会有带有 _chain 属性),则对运算结果挪用 chain 函数,使之能够继承链式挪用。

小结

Underscore 团体架构,或许说是基本完成也许就是这个模样,代码部份就讲到这了,接下去系列解读末了一篇,讲讲这段时候(险些也是用时半年了)的一些心得体会吧,没钱的就捧个人场吧!

    原文作者:fish
    原文地址: https://segmentfault.com/a/1190000007348258
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞