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中的方法進行具體解析,感謝大家的觀看,也希望能夠和大家互相交流學習,有什麼分析的不對的地方歡迎大家批評指出
參考資料
https://segmentfault.com/a/1190000000515420http://yalishizhude.github.io/2015/09/22/underscore-source/