前言

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. 工具:Entity Framework Profiler

    使用方式: 1.项目中添加引用:EntityFrameworkProfiler.Appender 2.应用程序启动方法中添加下面一行代码: HibernatingRhinos.Profiler.App ...

  2. linux下使用crontab实现定时PHP计划任务失败的原因分析

    这篇文章主要介绍了linux下使用crontab实现定时PHP计划任务失败的原因分析,需要的朋友可以参考下   很多人在linux下使用crontab实现PHP执行定时任务却未能成功,不能生成缓存.本 ...

  3. HTTP服务简介

    第1章 HTTP服务介绍 1.1 简述用户访网站流程 a 进行域名信息的DNS解析   dig +trace 获得www.oldboyedu.com  ip地址信息 b 进行与网站服务器建立连接,tc ...

  4. 磁盘管理之 raid 文件系统 分区

    第1章 RAID 磁盘阵列 1.1 使用raid的目的 1)获得更大的容量 2)让数据更安全 3)读写速度更快 1.2 raid0.raid1.raid5.raid10对比 磁头 0磁道 1扇区 前4 ...

  5. 一个“”字引发的痛苦经历

    (一篇老文章,还有点价值,特意整理了一下.由于涉及客户项目,已经进行了脱敏处理) 1 写在前面的话 虽然这个问题是有解决方案的,但我不建议大家提供给客户,理由见此. 2 问题描述 2010.10.12 ...

  6. Redis的使用初探

    Redis Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化. 与其它键值数据存储相比,Redis有一组相对丰富的数据类型. Redis可以将数据复制到任意数量的从机中 Redis的安装 官 ...

  7. 【吐槽】关于256个 class可以覆盖一个id的问题

    还是说今天下午面试的事情,被面试官问了 40多分钟的问题,我觉得丫 一定是从哪个网站down了几份面试题,自个儿整合了一下,然后挨个问,刚开始感觉哟,不错哦,面试官懂的蛮多的. 然后问到某个问题之后, ...

  8. 微信小程序入门(一)

    想必当你对官方文档了解地差不多的时候,一颗跃跃欲试的心就开始骚动了吧. 开发小程序之前的准备工作: 1).准备一个域名 2).准备一台云服务器 3).搭建小程序的后台,博主的小程序后台请求的的是自己写 ...

  9. 使用.net连接domino的尝试

    导入包含Domino命名空间的dll:                  } 这里需要提到的是dynamic cast 运行期绑定的代码,需要做类型转化.

  10. Centos6上进行Mysql5.6安装和主从复制部署

    系统:centos6 数据库:mysql5.6 服务器:两台,一主一从 一.Mysql5.6二进制版本的安装 Mysql的安装在有三种模式,第一种是yum安装,第二种是二进制模式的安装,第三种是源码编 ...