技术控

    今日:57| 主题:49101
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Underscore源码分析系列(1)

[复制链接]
曾经的年少轻狂 发表于 2016-10-8 23:09:02
138 4

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

Underscore源码分析系列(1)-1 (underscore,源码)

  说明

   本文是基于 underscore 1.8.3 来进行源码分析的。
  [code]_.VERSION = "1.8.3" ;[/code]  立即执行函数

   underscore 最外层是一个立即执行函数,如下:
  [code](function () {...}) (); // 立即执行函数[/code]  立即执行函数(IIFE)的主要特征是:
  1. 使用函数表达式声明一个函数
  2. 在其后使用括号直接调用
   使用立即函数可以创建一个独立的沙箱似的作用域,它是函数的一种特殊调用方式,利用了 JavaScript 的函数作用域的概念。这样可以防止其他代码对该函数内部造成影响,而且不会创造全局变量防止污染全局空间。
  全局命名空间

  [code]var
  root =
  (
    (
      (
        ((typeof(self)) == ("object")) // self表示window窗口自身,这是浏览器环境下的全局命名空间
        &&
        ((self.self) === (self)) // 如果存在self,判断self是否是自身引用,即window这一对象
      )
      &&
      (self) // 如果以上都满足,说明全局对象是window,并返回window作为root,这里self即window
    )
    ||
    (
      (
        ((typeof(global)) == ("object")) // global表示node环境下全局的命名空间
        &&
        ((global.global) === (global)) // 如果存在gloabl,判断global是否是自身引用
      )
      &&
      (global) // 如果以上都满足,说明全局对象是global,并返回global作为root
    )
  )
  ||
  (this); // 如果以上都不满足,直接返回this,这里应该处理既不是window这一浏览器环境,也不是global这一node环境的[/code]  该段代码主要用于确认环境的全局命名空间,并赋值给变量root。有趣的是,之前代码是这样的:
  [code]var root = this;[/code]   这引起了笔者的好奇,并进行了一定的研究,首先这一做法应该是出自学习了 lodash 的做法。它有如下两个好处:
  
       
  • 向前兼容严格模式,在严格模式下直接使用 this 会得到 undefined 。这是因为 ecma262 第5版中,为了防止人们误将非new出来的对象的this指向全局命名空间,特地将之设置为 undefined 。   
  • 用来支持 WebWorker ,在 WebWorker 里可以使用 self 但不能使用 window 。  
   另一个有趣的地方的是, selfglobal 使用 typeof 判断时使用 == 而非 === ,这样可以进行转义,因为全局命名空间不一定完全等价为对象,这跟浏览器的实现有关。
  防冲突

   防止与其他库对 _ 的使用冲突是十分重要的。 underscore 做了如下处理。
  [code]// 保存原有全局变量“_”
var previousUnderscore = root._;

// 使用noConflict方法返回自身
_.noConflict = function() {
  root._ = previousUnderscore;
  return this;
};[/code]  保存原型以及方法的快速引用

  [code]var
  ArrayProto = Array.prototype,
  ObjProto = Object.prototype;
var
  SymbolProto = ((typeof(Symbol)) !== ("undefined")) ? (Symbol.prototype) : (null);
var
  push = ArrayProto.push,
  slice = ArrayProto.slice,
  toString = ObjProto.toString,
  hasOwnProperty = ObjProto.hasOwnProperty;
var
  nativeIsArray = Array.isArray,
  nativeKeys = Object.keys,
  nativeCreate = Object.create;[/code]   其中值得注意的是, Symbolecma262 第6版才正式发布的,所以要先判断是否存在。
  对象创建的特殊处理

   为了处理 Object.create 的跨浏览器的兼容性, underscore 进行了特殊的处理。我们知道,原型是无法直接实例化的,因此我们先创建一个空对象,然后将其原型指向这个我们想要实例化的原型,最后返回该对象其一个实例。其代码如下:
  [code]var Ctor = function() {};  // 用于代理原型转换的空函数

var baseCreate = function(prototype) {
  if (!(_.isObject(prototype))) return {}; // 如果参数不是对象,直接返回空对象
  if (nativeCreate) return nativeCreate(prototype); // 如果原生的对象创建可以使用,返回该方法根据原型创建的对象
   
  // 处理没有原生对象创建的情况
  Ctor.prototype = prototype;  // 将空函数的原型指向要使用的原型
  var result = new Ctor();  // 创建一个实例
  Ctor.prototype = null;  // 恢复Ctor的原型供下次使用
  return result;  // 返回该实例
};[/code]  初始化

  [code]var _ = function(obj) {
  if (obj instanceof _) return obj;  // 如果参数是underscore的一个实例,就直接返回该参数
  if (!(this instanceof _)) return new _(obj);  // 实例化
  this._wrapped = obj;  // 将该实例保存
};[/code]   这里是为了不用用户自己实例化 underscore ,并将该实例保存。
  [code] if (typeof exports != 'undefined' && !exports.nodeType) {
  if (typeof module != 'undefined' && !module.nodeType && module.exports) {
  exports = module.exports = _;
  }
  exports._ = _;
} else {
  root._ = _;
}[/code]   这里笔者翻译一下原有注释。这一部分代码是为了在 node.js 环境下将 underscore 作为一个模块使用,并向后兼容旧版的模块 API ,即 require 。如果在浏览器环境中,则将 underscore 以_暴露到全局。值得注意的是使用 nodeType 来确保 exportsmodule 并不是HTML的元素。
  回调处理

   underscore 有大量需要回调的函数,因此对于回调进行了特殊的处理。首先存在一个 optimizeCb 函数,它对正常传入的函数进行一层包装处理,这样可以在更好的重复使用,保证上下文即this正确。
  [code]var optimizeCb = function(func, context, argCount) {
  if (context === void 0) return func;  // void 0返回undefined,即未传入上下文信息时直接返回相应的函数
  switch (argCount == null ? 3 : argCount) {  // 如果传入了argCount,那么参数数量为argCount,如果传入等价为null,则为3,包括未传值得情况
    // 1个参数的时候,只需要传递当前值
    case 1: return function(value) {
      return func.call(context, value);  
    };
    // 并没有2个参数的时候,因为目前并没有用到2个参数的时候
   
    // 3个参数的时候,分别是当前值、当前索引以及整个集合
    case 3: return function(value, index, collection) {
      return func.call(context, value, index, collection);
    };
    // 4个参数的时候,分别是累计值、当前值、当前索引以及整个集合
    case 4: return function(accumulator, value, index, collection) {
      return func.call(context, accumulator, value, index, collection);
    };
  }
  // 如果都不符合上述的任一条件,直接使用apply调用相关函数
  return function() {
    return func.apply(context, arguments);
  };
};[/code]  接下来,是针对集合迭代的回调处理。
  [code](function () {...}) (); // 立即执行函数0[/code]  杂项

  在开始集合函数编写前还有一些处理。
  [code](function () {...}) (); // 立即执行函数1[/code]
友荐云推荐




上一篇:10 Tips in Picking a Technology Stack for Web Developers
下一篇:每日一博|Zookeeper -- 管理分布式环境中的数据
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

lihdm 发表于 2016-10-9 03:23:09
来自水星的曾经的年少轻狂
回复 支持 反对

使用道具 举报

yiyizhang 发表于 2016-10-12 02:08:19
男女关系处理得好会传出佳话,处理得不好会传出闲话。
回复 支持 反对

使用道具 举报

qcwvw 发表于 2016-11-12 18:09:36
我曾经跟一个人无数次的擦肩而过,衣服都擦破了,也没擦出火花。
回复 支持 反对

使用道具 举报

unixnow 发表于 2016-11-13 10:57:36
你女儿在我手上,我不是你女婿。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表