前言

underscore是最适合初级人士阅读的源码,在阅读源码时,有一些有趣的实现,记录如下。

基于underscore1.8.3。

留存root

// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{}; // Save the previous value of the `_` variable.
var previousUnderscore = root._; // .......
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};

在浏览器情况下,self是window自身的引用。上面的语法主要是为了保证在sever端和服务端都能正常获得根对象。

将root._ 存起来,是为了防止命名冲突。调用noConflict方法,就能把原来的 _ 恢复,然后重新赋值到不冲突的变量上即可。

保留原生方法、减少变量查询

在underscore源码常看到会将一些常用的方法保留起来。

// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; // Create quick reference variables for speed access to core prototypes.
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;

这样做的好处有两个:

  1. 减小*.min.js的体积。 在压缩时,some.func1只会被压缩成a.func1。如果把一个对象上常用的方法存为一个变量func1,那么压缩后将节省很多字节。
  2. 加快变量访问速度。

    在实际中,点操作符的使用会使得JavaScript引擎检索该对象下的所有成员。如果嵌套越深,那么读取速度越慢,花费时间越久。如果不是该对象的实例属性,引擎甚至要去检索原型链,将更加耗费时间。

题外话:实际上,为了更好得提高性能,通常将变量保存到局部作用域,检索将会加快。

链式调用

var chainResult = function(instance, obj) {
// 如果_chain为true,则return一个加了链式属性的underscore对象。
return instance._chain ? _(obj).chain() : obj;
};
// Add your own custom functions to the Underscore object.
// 可以把自己写的扩展方法通过mixin加入到underscore (_) 上。
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return chainResult(this, func.apply(_, args));
};
});
return _;
}; // Add all of the Underscore functions to the wrapper object.
// 对underscore使用mixin,可以将全部实例方法挂载到原型上。
_.mixin(_); // 链式调用方法,不过是加了一个Boolean型开关,来对返回值做判断
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};

_.mixin方法用来把obj上的方法,都内置到下划线 _ 上,相当于jquery的extends方法。

此处调用 _ mixin( _ );实际上,是将 _ 上的方法,都挂载到 _ .prototype上,以便于之后的链式调用。

再来关注一下 _.chain这个方法,调用之后会返回一个underscore对象,并且把该对象的 _ chain属性赋为true。在chainResult这个方法里,会对当前的这个实例的 _ chain属性进行判断,如果调用了chain方法,就认为接下来会进行链式调用,就会将这个实例包裹之后,继续返回。

链式调用的关键就在于,函数return原对象

构造函数

var _ = function(obj) {
// 如果是underscore的实例,就直接返回obj
if (obj instanceof _) return obj;
// 如果this不是underscore的实例,就new一个新的underscore实例并返回
if (!(this instanceof _)) return new _(obj);
// 将this._wrapped属性置为obj
this._wrapped = obj;
};

需要注意第二步,this的指向,因为如果直接调用 _ 函数,则this指向为window,使用new构造函数,this指向为新创建的对象。

一些函数

接下来对一些函数做分析。

optimizeCb

这个方法是一个优化方法,根剧不同的参数个数,返回不同的调用方式。

好处有三:

  1. call比apply性能优异,因为apply传入数组,也是用调用底层的CALL方法,可以查看ecmascript262规范
  2. 因为arguments这个类数组对象较为消耗性能,所以不直接使用arguments来做判断。
  3. 绑定上下文。

isArray

_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};

目前判断数组的方法,较为公认的做法就是通过toString,查看是否是[object Array]。在ES5之后,原生带有isArray方法,兼容性不是很完善,IE9之后支持。可以把这个改一下,作为polyfill。

在zepto中的isArray实现稍有不同:

isArray = Array.isArray ||
function(object){ return object instanceof Array }

这两种方法有所区别,zepto的实现在iframe的情况下会有bug,具体参见这篇博客

不过由于移动端通常不会使用iframe,所以,不会有特别大的问题。

underscore源码解析(一)的更多相关文章

  1. underscore源码解析 (转载)

    转载出自http://www.cnblogs.com/human/p/3273616.html (function() { // 创建一个全局对象, 在浏览器中表示为window对象, 在Node.j ...

  2. underscore源码解析

    (function() { // 创建一个全局对象, 在浏览器中表示为window对象, 在Node.js中表示global对象 var root = this; // 保存"_" ...

  3. #啃underscore源码 一、root对象初始化部分

    最近由于比赛要交了,以及工作室屯了各种项目,实在忙不过来刷题,所以很久没更blog了(良心痛),现在自己的水平还是渣代码堆砌 + 简单的增删改查(悲伤) 所以痛定思痛,决定之后的任务是先补学校课堂的知 ...

  4. underscore.js源码解析(五)—— 完结篇

    最近公司各种上线,所以回家略感疲惫就懒得写了,这次我准备把剩下的所有方法全部分析完,可能篇幅过长...那么废话不多说让我们进入正题. 没看过前几篇的可以猛戳这里: underscore.js源码解析( ...

  5. underscore.js源码解析(四)

    没看过前几篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二) underscore.js源码解析(三) underscore.js源码GitHub地 ...

  6. underscore.js源码解析(三)

    最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续. 没看过前两篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二 ...

  7. underscore.js源码解析(二)

    前几天我对underscore.js的整体结构做了分析,今天我将针对underscore封装的方法进行具体的分析,代码的一些解释都写在了注释里,那么废话不多说进入今天的正文. 没看过上一篇的可以猛戳这 ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. 【原创】backbone1.1.0源码解析之Collection

    晚上躺在床上,继续完成对Backbone.Collection的源码解析. 首先讲讲它用来干嘛? Backbone.Collection的实例表示一个集合,是很多model组成的,如果用model比喻 ...

随机推荐

  1. Python迭代

    本篇将介绍Python的迭代,更多内容请参考:Python学习指南 简介 在Python中,如果给定一个list或者tuple,我们可以通过for循环来遍历这个list或者tuple,这种遍历我们称为 ...

  2. Visual paradigm软件介绍

    Visual paradigm软件介绍 说起Visual Paradigm你可能并不陌生,因为此前有一款功能强大的UML软件叫Visual Paradigm for UML,在这款软件在v11.1的时 ...

  3. Leetcode题解(27)

    86. Partition List 题目 分析:题目要求将链表划分为两部分,前半部分小于x,后半部分大于等于x,并且各个数之间的相对顺序不变. 解题思路是:从头开始扫描链表,找打第一个大于等于x的数 ...

  4. Color the Ball(懵逼题)

    Color the Ball Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...

  5. 交换知识 VLAN VTP STP 单臂路由

    第1章 交换基础 1.1 园区网分层结构 层次 作用 出口层 广域网接入 出口策略 带宽控制 核心层 高速转发 服务器接入 路由选择 汇聚层 流量汇聚 链路冗余 设备冗余 路由选择 接入层 用户接入 ...

  6. 使用sklearn进行数据挖掘-房价预测(6)—模型调优

    通过上一节的探索,我们会得到几个相对比较满意的模型,本节我们就对模型进行调优 网格搜索 列举出参数组合,直到找到比较满意的参数组合,这是一种调优方法,当然如果手动选择并一一进行实验这是一个十分繁琐的工 ...

  7. java爬虫--jsoup简单的表单抓取案例

    分析需求: 某农产品网站的农产品价格抓取 网站链接:点击打开链接 页面展示如上: 标签展示如上: 分析发现每日价格行情包括了蔬菜,水果,肉等所有的信息,所以直接抓每日行情的内容就可以实现抓取全部数据. ...

  8. 【Aladdin Unity3D Shader编程】之三 光照模型(二)

    高光反射模型 Specular=直射光*pow(cosθ,高光的参数) θ:是反射光和视野方向的夹角 编写高光反射Shader Shader "AladdinShader/07 Specul ...

  9. kafka单机模式部署安装,zookeeper启动

    在root的用户下 1):前提 安装JDK环境,设置JAVA环境变量 2):下载kafka,命令:wget  http://mirrors.shuosc.org/apache/kafka/0.10.2 ...

  10. js实现查找字符串中最多的字符的个数

    用hash table实现.key是字符,value是字符个数. var hashTable={}; var str="fjsdeiuwidshjfhjsksghfjhsjjskalsk&q ...