一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个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中的方法进行具体解析,感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出
来源:https://www.cnblogs.com/WhiteBlade/p/5215434.html