underscore.js源码解析(一)
一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个underscore.js由于这个比较简短,一千多行,而且读起来容易一些,所以就决定是它了,那废话不多说开始我们的源码学习。
underscore.js源码GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js本文解析的underscore.js版本是1.8.3
结构解析
我们先从整体的结构开始分析(其中加入了注释加以解释说明)
1 (function() { 2 // 创建一个root对象,在浏览器中表示为window(self)对象,在Node.js中表示global对象, 3 // 之所以用用self代替window是为了支持Web Worker 4 var root = typeof self == 'object' && self.self === self && self || 5 typeof global == 'object' && global.global === global && global || 6 this; 7 // 保存"_"(下划线变量)被覆盖之前的值 8 var previousUnderscore = root._; 9 // 原型赋值,便于压缩 10 var ArrayProto = Array.prototype, ObjProto = Object.prototype; 11 // 将内置对象原型中的常用方法赋值给引用变量,以便更方便的引用 12 var push = ArrayProto.push, 13 slice = ArrayProto.slice, 14 toString = ObjProto.toString, 15 hasOwnProperty = ObjProto.hasOwnProperty; 16 // 定义了一些ECMAScript 5方法 17 var nativeIsArray = Array.isArray, 18 nativeKeys = Object.keys, 19 nativeCreate = Object.create; 20 //跟神马裸函数有关,我也不清楚什么意思,有知道可以告诉我 21 var Ctor = function(){}; 22 // 创建一个下划线对象 23 var _ = function(obj) { 24 // 如果在"_"的原型链上(即_的prototype所指向的对象是否跟obj是同一个对象,要满足"==="的关系) 25 if (obj instanceof _) return obj; 26 // 如果不是,则构造一个 27 if (!(this instanceof _)) return new _(obj); 28 // 将underscore对象存放在_.wrapped属性中 29 this._wrapped = obj; 30 }; 31 // 针对不同的宿主环境, 将Undersocre的命名变量存放到不同的对象中 32 if (typeof exports != 'undefined' && !exports.nodeType) {//Node.js 33 if (typeof module != 'undefined' && !module.nodeType && module.exports) { 34 exports = module.exports = _; 35 } 36 exports._ = _; 37 } else {//浏览器 38 root._ = _; 39 } 40 //版本号 41 _.VERSION = '1.8.3'; 42 //下面是各种方法以后的文章中会具体说明 43 . 44 . 45 . 46 . 47 . 48 . 49 // 创建一个chain函数,用来支持链式调用 50 _.chain = function(obj) { 51 var instance = _(obj); 52 //是否使用链式操作 53 instance._chain = true; 54 return instance; 55 }; 56 // 返回_.chain里是否调用的结果, 如果为true, 则返回一个被包装的Underscore对象, 否则返回对象本身 57 var chainResult = function(instance, obj) { 58 return instance._chain ? _(obj).chain() : obj; 59 }; 60 // 用于扩展underscore自身的接口函数 61 _.mixin = function(obj) { 62 //通过循环遍历对象来浅拷贝对象属性 63 _.each(_.functions(obj), function(name) { 64 var func = _[name] = obj[name]; 65 _.prototype[name] = function() { 66 var args = [this._wrapped]; 67 push.apply(args, arguments); 68 return chainResult(this, func.apply(_, args)); 69 }; 70 }); 71 }; 72 _.mixin(_); 73 // 将Array.prototype中的相关方法添加到Underscore对象中, 这样Underscore对象也可以直接调用Array.prototype中的方法 74 _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 75 //方法引用 76 var method = ArrayProto[name]; 77 _.prototype[name] = function() { 78 // 赋给obj引用变量方便调用 79 var obj = this._wrapped; 80 // 调用Array对应的方法 81 method.apply(obj, arguments); 82 if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; 83 //支持链式操作 84 return chainResult(this, obj); 85 }; 86 }); 87 88 // 同上,并且支持链式操作 89 _.each(['concat', 'join', 'slice'], function(name) { 90 var method = ArrayProto[name]; 91 _.prototype[name] = function() { 92 //返回Array对象或者封装后的Array 93 return chainResult(this, method.apply(this._wrapped, arguments)); 94 }; 95 }); 96 //返回存放在_wrapped属性中的underscore对象 97 _.prototype.value = function() { 98 return this._wrapped; 99 }; 100 101 // 提供一些方法方便其他情况使用 102 _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; 103 _.prototype.toString = function() { 104 return '' + this._wrapped; 105 }; 106 107 // 对AMD支持的一些处理 108 if (typeof define == 'function' && define.amd) { 109 define('underscore', [], function() { 110 return _; 111 }); 112 } 113 }());
总结
具体分析在上面源码中的注释里写的已经很详细了,下面再从头理顺一下整体的结构:
首先underscore包裹在一个匿名自执行的函数当中
内部定义了一个"_"变量
将underscore中的相关方法添加到_原型中,创建的_对象就具备了underscore方法
将Array.prototype中的相关方法添加到Underscore对象中, 这样Underscore对象也可以直接调用Array.prototype中的方法
之后的文章中,我会针对underscore中的方法进行具体解析,感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出