對的,讓你所見,又開始造輪子了。哈哈,造輪子咱們是認真的~
源碼閱讀是必須的,Underscore是由於剛剛學習整理了一波函數式編程,加上本身曾經沒有太多閱讀源碼的經驗,先拿Underscore練練手,跟着前輩們走一走,學一學。也相同時可以夯實js基礎,從源碼中學習到更多的編碼技巧css
Underscore源碼閱讀大體按照官方文檔來編寫.儘可能的說明每個函數的寫法,但願本身能夠從中能夠收穫大神的編碼功力。 java
Underscore源碼+註釋地址
(function(){}())
常規操做哈,跟jQuery一毛同樣,經過IIFE來包裹業務邏輯,目的簡單:一、避免全局污染。二、保護隱私git
var root = typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global || this || {}; var previousUnderscore = root._;
經過global和self來判斷是node環境仍是window環境,說白了,就是爲了拿到全局變量。由於咱們須要一個全局的變量_,因此爲了防止衝突,咱們這裏拿到root後,先暫存下以前的root._github
var ArrayProto = Array.prototype, ObjProto = Object.prototype; var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeCreate = Object.create; var Ctor = function(){}; var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; if (typeof exports != 'undefined' && !exports.nodeType) { if (typeof module != 'undefined' && !module.nodeType && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } _.VERSION = '1.8.3';
因爲Underscore自己依賴不少原生js的方法,因此這裏爲了不原型鏈的查找性能消耗,Underscore經過局部變量來保存一些經常使用的對象和方法。既能夠提高性能,減小對象成員訪問深度也能夠減小代碼的冗長。編程
下面的Ctor和_ 是爲了面向對象而準備的。設計模式
var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; var builtinIteratee; var cb = function(value, context, argCount) { if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value) && !_.isArray(value)) return _.matcher(value); return _.property(value); }; _.iteratee = builtinIteratee = function(value, context) { return cb(value, context, Infinity); };
這裏的迭代,咱們須要理清楚一個概念,在Underscore中,咱們須要改變那種命令式的編程方式,具體的能夠看我以前寫的關於函數式編程的文章哈。數組
因此這裏想說的就是關於遍歷迭代的東西。瀏覽器
var results = _.map([1,2,3],function(elem){ return elem*2; }); // => [2,4,6] _.map = _.collect = function (obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length); // 定長初始化數組 for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; };
咱們傳遞給的 _.map 的第二個參數就是一個 iteratee,他多是函數,對象,甚至是字符串。app
在上面的分析中,咱們知道,當傳入的 value 是一個函數時,value 還要通過一個叫 optimizeCb 的內置函數才能得到最終的 iteratee:
var cb = function (value, context, argCount) { // ... if (_.isFunction(value)) return optimizeCb(value, context, argCount); // ... };
因此此處的optimizeCb必然是優化回調的做用了。
// 優化回調的函數,遍歷 var optimizeCb = function(func, context, argCount) { // void 0 會返回真正的undefined 此處確保上下文的存在 if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { // argCount爲0時候,迭代過程當中,咱們只須要這個value就能夠了 return func.call(context, value); }; // The 2-parameter case has been omitted only because no current consumers // 3個參數(值,索引,被迭代集合對象). case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; // 4個參數(累加器(好比reducer須要的), 值, 索引, 被迭代集合對象) case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; };
總體的代碼很是清晰,待優化的回調函數func,上下文context以及迭代回調須要的參數個數。
上面的這個優化的回調涉及到不一樣地方使用的不一樣迭代。這裏暫時 先放一放。等過了一遍源碼後,看到每個用到迭代的地方,在回頭來看,就會明白不少。
在 ES6中,咱們定義不定參方法的時候能夠這麼寫
let a = (b,...c)=>{ console.log(b,c); }
可是在此以前,Underscore實現了本身的reset,使用以下:
function a(a,b,c,d,e){ console.log(a,b,c,d,e) } let aa = restArgs(a);//let aa = restArgs(a,4) aa(1,2,3,4,5,6,7,8,8)
看下restArgs的實現:
var restArgs = function(func, startIndex) { //未傳則取形參個數減一 startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { // 多傳了幾個參數 //length爲多傳了幾個參數 var length = Math.max(arguments.length - startIndex, 0), rest = Array(length), index = 0; for (; index < length; index++) { rest[index] = arguments[index + startIndex]; } //優化。注意rest參數老是最後一個參數, 不然會有歧義 switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } //撇去經常使用的startIndex,這裏循環 //先拿到前面參數 var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } //拿到後面的數組 args[startIndex] = rest; return func.apply(this, args); }; };
關於面向對象,這裏不作過多解釋了,能夠參考個人另外一篇文章:
javasript設計模式之面向對象。
咱們直接看他的繼承實現吧
var Ctor = function(){}; // 定義了一個用於繼承的內部方法 var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; // nativeCreate = Object.create; if (nativeCreate) return nativeCreate(prototype); Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; };
es5 中,咱們有一種建立對象的方式,Object.create 。
function Animal(name){ this.name = name; } Animal.prototype.eat = function(){ console.log(this.name,'鳥爲食亡'); } var dog = Object.create(Animal.prototype); dog.name = "毛毛"; dog.eat();
ok,大概從上你們就看出來create的做用了。
baseCrate中,首先判斷是否爲對象,不然退出。瀏覽器能力檢測是否具有Object.create方法,具有則用。不然採用寄生式繼承建立對象。須要注意的是,baseCreate僅僅支持原型繼承,而不能像Object.create那樣傳遞屬性列表。
開篇簡單的介紹Collection Functions上面的代碼部分。在介紹Collection Function每一個方法實現以前,咱們將在下一篇看一下一些工具方法的編寫方式。
的確在造造輪子,只是更想本身擼一遍優秀代碼。