资讯专栏INFORMATION COLUMN

探究underscore源码(一)

CloudwiseAPM / 2634人阅读

摘要:什么鬼结合上面的函数,貌似可以看到每次调用函数时都会判断一次是否等于。主要原理是利用回调函数来处理调用方法传入的参数。

本文基于underscore v1.8.3版本

源头

一直想学习一下类库的源码,jQuery刚刚看到选择器那块,直接被那一大块正则搞懵逼了。经过同事的推荐,选择了underscore来作为类库研究的起点。

闭包

所有函数都在一个闭包内,避免污染全局变量,这没什么特殊的,略过。。。

(function() { ...    
}());
全局对象的获取

先看下面一段代码:

var root = typeof self == "object" && self.self === self && self ||
           typeof global == "object" && global.global === global && global ||
           this;

self是什么鬼?globalthis都能够猜出来是全局变量,这个self从哪里冒出来的?

第一眼看到这样的代码很困惑,感觉压根没有头绪。但是如果你打开chrome的控制台,神奇的事情发生了

其实查看源码的注释,我们也能看出来这段代码的作用:在不一样的环境里面获取当前全局对象this

self | window:浏览器

global:服务端

this:某些虚拟机

为了压缩所做的原型赋值

源码中有将对象的原型链赋值给一个变量的做法:

var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== "undefined" ? Symbol.prototype : null;

一开始我并没明白这么做的优势,代码不都一样吗?

参考注释并且上网查资料才知道原因:为了压缩

举个例子,Array.prototype是没有办法经过压缩的,Array,prototype这些,如果改了,浏览器就无法识别这些字段了。

但经过类似上面代码的处理,ObjProto经过压缩就能变成变量a,那么原来的代码就会变成a.xxx

我们平常写的代码也可以进行类似上面的处理,只要代码的复用超过两次,就可以考虑将其赋值给一个变量了。

this值统一处理

this在类库中的应用很广泛,undersocre采用了一个内部函数来处理this

  var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      // The 2-parameter case has been omitted only because no current consumers
      // made use of it.
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };

注意到上面的case语句没有2的情况,看其注释基本就能明白,这是因为没有使用到2的情况。

上面函数的最后一个参数argCount是用来指定参数个数:

接受单值的情况

已取消

迭代器函数

reduce函数

callback的统一处理
var cb = function(value, context, argCount) {
    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
    if (value == null) return _.identity;
    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
    if (_.isObject(value)) return _.matcher(value);
    return _.property(value);
  };

cb就是callback的简写,看函数的注释的意思是:内部函数,用来生成可应用于集合内每个元素的回调函数,返回预期的结果,具体应用向下看。

iteratee什么鬼?
_.iteratee = builtinIteratee = function(value, context) {
    return cb(value, context, Infinity);
  };

结合上面的cb函数,貌似可以看到每次调用cb函数时都会判断一次_.iteratee是否等于builtinIteratee

如果不等于则调用_.iteratee函数,让_.iteratee = builtinIteratee,再继续执行cb函数。

结合注释,猜测这个函数的作用应该是防止用户自己定义iteratee函数。

restArgs又一个基础函数
var restArgs = function(func, startIndex) {
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    return function() {
      var length = Math.max(arguments.length - startIndex, 0),
          rest = Array(length),
          index = 0;
      for (; index < length; index++) {
        rest[index] = arguments[index + startIndex];
      }
      switch (startIndex) {
        case 0: return func.call(this, rest);
        case 1: return func.call(this, arguments[0], rest);
        case 2: return func.call(this, arguments[0], arguments[1], rest);
      }
      var args = Array(startIndex + 1);
      for (index = 0; index < startIndex; index++) {
        args[index] = arguments[index];
      }
      args[startIndex] = rest;
      return func.apply(this, args);
    };
  };

这个函数作用就类似ES6里面的rest params,这个函数主要是在官网分类里面的collections用到,例如:invoke

主要原理是利用回调函数来处理调用方法传入的参数。

创建继承函数
  var baseCreate = function(prototype) {
    if (!_.isObject(prototype)) return {};
    if (nativeCreate) return nativeCreate(prototype);
    Ctor.prototype = prototype;
    var result = new Ctor;
    // 创建 result 之后清空 Ctor 的原型链,防止 全局变量 Ctor 的原型链污染
    Ctor.prototype = null;
    return result;
  };

主要原理就是利用 Ctor 做一个中介,创建继承函数并返回后再清空Ctor的原型链,防止原型链污染

取对象的属性值
 var property = function(key) {
    return function(obj) {
      return obj == null ? void 0 : obj[key];
    };
  };

这个方法浅显易懂,如果传入的objectnull,则返回 undefined,否则返回属性值。

其它的全局变量
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property("length");
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX;
  };

主要是帮助collection的方法来判定某个变量是否为collection

后记

看完这一段,感觉依旧有许多疑问。主要是因为这些全局定义的变量的使用场景没有深究,更直白一些,就是没有按照代码的线索专研下去。希望在接下来的主要API的分析中能够在好好回顾上面的那些函数以及变量

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90959.html

相关文章

  • 探究underscore源码(二)

    摘要:源码如下通过来判断到底通过来区分对象以及数组。传入回调函数的参数分别为对象键值对中的值或者数组中的序号值对象键值对中的键或者数组中的相应序号举个例子,传入回调的参数依次为如果是数组,则传入参数依次为三这几个方法都是利用一个核心函数。 一、_.each 一开始我并没有以为_.each这个方法会有多大的用处,不就是一个遍历嘛~ 但当我利用自己测试这个函数的时候,发现了一件大事 under...

    jeyhan 评论0 收藏0
  • JavaScript专题系列20篇正式完结!

    摘要:写在前面专题系列是我写的第二个系列,第一个系列是深入系列。专题系列自月日发布第一篇文章,到月日发布最后一篇,感谢各位朋友的收藏点赞,鼓励指正。 写在前面 JavaScript 专题系列是我写的第二个系列,第一个系列是 JavaScript 深入系列。 JavaScript 专题系列共计 20 篇,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里...

    sixleaves 评论0 收藏0
  • javascript知识点

    摘要:模块化是随着前端技术的发展,前端代码爆炸式增长后,工程化所采取的必然措施。目前模块化的思想分为和。特别指出,事件不等同于异步,回调也不等同于异步。将会讨论安全的类型检测惰性载入函数冻结对象定时器等话题。 Vue.js 前后端同构方案之准备篇——代码优化 目前 Vue.js 的火爆不亚于当初的 React,本人对写代码有洁癖,代码也是艺术。此篇是准备篇,工欲善其事,必先利其器。我们先在代...

    Karrdy 评论0 收藏0
  • 你要看看这些有趣的函数方法吗?

    前言 这是underscore.js源码分析的第六篇,如果你对这个系列感兴趣,欢迎点击 underscore-analysis/ watch一下,随时可以看到动态更新。 下划线中有非常多很有趣的方法,可以用比较巧妙的方式解决我们日常生活中遇到的问题,比如_.after,_.before,_.defer...等,也许你已经用过他们了,今天我们来深入源码,一探究竟,他们到底是怎么实现的。 showIm...

    melody_lql 评论0 收藏0

发表评论

0条评论

CloudwiseAPM

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<