underscore是最適合初級人士閱讀的源碼,在閱讀源碼時,有一些有趣的實現,記錄以下。
基於underscore1.8.3。javascript
// 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方法,就能把原來的 _ 恢復,而後從新賦值到不衝突的變量上便可。html
在underscore源碼常看到會將一些經常使用的方法保留起來。java
// 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;
這樣作的好處有兩個:jquery
題外話:實際上,爲了更好得提升性能,一般將變量保存到局部做用域,檢索將會加快。數組
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方法,就認爲接下來會進行鏈式調用,就會將這個實例包裹以後,繼續返回。app
鏈式調用的關鍵就在於,函數return原對象。ecmascript
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指向爲新建立的對象。函數
接下來對一些函數作分析。性能
這個方法是一個優化方法,根劇不一樣的參數個數,返回不一樣的調用方式。
好處有三:
_.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,因此,不會有特別大的問題。