一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个underscore.js由于这个比较简短,一千多行,而且读起来容易一些,所以就决定是它了,那废话不多说开始我们的源码学习。

underscore.js源码GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js
 
本文解析的underscore.js版本是1.8.3
 

结构解析

 
我们先从整体的结构开始分析(其中加入了注释加以解释说明)
 
 (function() {
// 创建一个root对象,在浏览器中表示为window(self)对象,在Node.js中表示global对象,
// 之所以用用self代替window是为了支持Web Worker
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this;
// 保存"_"(下划线变量)被覆盖之前的值
var previousUnderscore = root._;
// 原型赋值,便于压缩
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
// 将内置对象原型中的常用方法赋值给引用变量,以便更方便的引用
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// 定义了一些ECMAScript 5方法
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
//跟神马裸函数有关,我也不清楚什么意思,有知道可以告诉我
var Ctor = function(){};
// 创建一个下划线对象
var _ = function(obj) {
// 如果在"_"的原型链上(即_的prototype所指向的对象是否跟obj是同一个对象,要满足"==="的关系)
if (obj instanceof _) return obj;
// 如果不是,则构造一个
if (!(this instanceof _)) return new _(obj);
// 将underscore对象存放在_.wrapped属性中
this._wrapped = obj;
};
// 针对不同的宿主环境, 将Undersocre的命名变量存放到不同的对象中
if (typeof exports != 'undefined' && !exports.nodeType) {//Node.js
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {//浏览器
root._ = _;
}
//版本号
_.VERSION = '1.8.3';
//下面是各种方法以后的文章中会具体说明
.
.
.
.
.
.
// 创建一个chain函数,用来支持链式调用
_.chain = function(obj) {
var instance = _(obj);
//是否使用链式操作
instance._chain = true;
return instance;
};
// 返回_.chain里是否调用的结果, 如果为true, 则返回一个被包装的Underscore对象, 否则返回对象本身
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
// 用于扩展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));
};
});
};
_.mixin(_);
// 将Array.prototype中的相关方法添加到Underscore对象中, 这样Underscore对象也可以直接调用Array.prototype中的方法
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
//方法引用
var method = ArrayProto[name];
_.prototype[name] = function() {
// 赋给obj引用变量方便调用
var obj = this._wrapped;
// 调用Array对应的方法
method.apply(obj, arguments);
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
//支持链式操作
return chainResult(this, obj);
};
}); // 同上,并且支持链式操作
_.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
//返回Array对象或者封装后的Array
return chainResult(this, method.apply(this._wrapped, arguments));
};
});
//返回存放在_wrapped属性中的underscore对象
_.prototype.value = function() {
return this._wrapped;
}; // 提供一些方法方便其他情况使用
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
_.prototype.toString = function() {
return '' + this._wrapped;
}; // 对AMD支持的一些处理
if (typeof define == 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
}());

总结

具体分析在上面源码中的注释里写的已经很详细了,下面再从头理顺一下整体的结构:
 
首先underscore包裹在一个匿名自执行的函数当中
内部定义了一个"_"变量
将underscore中的相关方法添加到_原型中,创建的_对象就具备了underscore方法
将Array.prototype中的相关方法添加到Underscore对象中, 这样Underscore对象也可以直接调用Array.prototype中的方法

之后的文章中,我会针对underscore中的方法进行具体解析,感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出

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

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

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

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

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

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

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

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

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

  5. underscore.js源码解析【'_'对象定义及内部函数】

    (function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in ...

  6. underscore.js源码解析【对象】

    // Object Functions // ---------------- // Keys in IE < 9 that won't be iterated by `for key in . ...

  7. underscore.js源码解析【函数】

    // Function (ahem) Functions // ------------------ // Determines whether to execute a function as a ...

  8. underscore.js源码解析【数组】

    // Array Functions // --------------- // Get the first element of an array. Passing **n** will retur ...

  9. underscore.js源码解析【集合】

    // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `f ...

随机推荐

  1. linux 文本操作

    sed -i 直接修改 sed 是负责插入替换删除字符串操作. sed -n '/11/p' 11.txt |sed  's/11/33333/g'  查找11并替换11位3333 sed  's/1 ...

  2. Javaweb学习(二):Http通信协议

      当我们开始jsp/servlet编程之旅之前,我们还需要知道一些关于网络通讯方面的一些知识.这样能更加有助于我们的理解,希望大家能看懂我的描述,而不至于在学习的路上一知半解.(手动比❤) 认识Ht ...

  3. openlayer3 基础学习一创建&显示地图

    <!doctype html> <html lang="en"> <head> <link rel="stylesheet&qu ...

  4. 卷积神经网络入门:LeNet5(手写体数字识别)详解

    第一张图包括8层LeNet5卷积神经网络的结构图,以及其中最复杂的一层S2到C3的结构处理示意图. 第二张图及第三张图是用tensorflow重写LeNet5网络及其注释. 这是原始的LeNet5网络 ...

  5. Git提交分支

    Git提交分支操作 1.git add 命令告诉 Git 开始对这些文件进行跟踪 git add . 2.然后提交 git commit -m'这是注释信息' 3.git pull命令用于从另一个存储 ...

  6. vlanif和vlan路由

    配置基于接口划分VLAN示例(接入层设备作为网关) 简介 划分VLAN的方式有:基于接口.基于MAC地址.基于IP子网.基于协议.基于策略(MAC地址.IP地址.接口).其中基于接口划分VLAN,是最 ...

  7. C#中抽象类(abstract)和接口(interface)的实现

    抽象类 抽象方法是没有代码实现的方法,使用abstract关键字修饰: 抽象类是包含0到多个抽象方法的类,其不能实例化.含有抽象方法的类必须是抽象类,抽象类中也可以包含非抽象方法: 重写抽象类的方法用 ...

  8. MyBatis insert操作插入,返回主键from官方

    下面就是 insert,update 和 delete 语句的示例: <insert id="insertAuthor" parameterType="domain ...

  9. $Gauss$消元

    $Gauss$消元 今天金牌爷来问我一个高消的题目,我才想起来忘了学高消... 高斯消元用于解线性方程组,也就是形如: $\left\{\begin{matrix}a_{11}x_1+a_{12}x_ ...

  10. [2018-12-15] Hello World!

    这个blog以后就用来发oi相关的算法与数据结构了 还可能想学习一点web前端的知识和一些与计算机有关的软件和技术 可能有空大概会试试搭建blog以及一些各种软件和c++以外的玩意