說明
一、源碼結構通覽,簡單註釋說明
二、經過調用方法講解核心代碼邏輯css
爲了方便比對源碼,按源碼的結構順序展現。
underscore是個輕量級的工具庫,大部分代碼是實現特定功能以函數的形式存在,自己會比較簡單,沒對方法具體說明,可直接參考underscore中文文檔jquery
(function() { var root = this; var previousUnderscore = root._; //原型賦值給變量,好處是方便壓縮 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; //方便直接調用原型上的方法 var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind, nativeCreate = Object.create; var Ctor = function(){}; //判斷obj是不是_的實例,是返回實例,不是下一步; //判斷this是否爲_的實例,是把obj賦於this實例的變量_wrapped //不是返回一個新實例,執行這個新實例時,新實例裏面的this指向該新實例。 //把obj賦於新實例的變量_wrapped var _ = function(obj) {}; //根據環境將_賦值給不一樣變量 if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } _.VERSION = '1.8.3'; //void 0 是undefined的意思 //context === void 0返回func; //根據argCount的值返回不一樣的函數 var optimizeCb = function(func, context, argCount) {}; //根據value類型返回不一樣函數 var cb = function(value, context, argCount) {}; //調用cb() _.iteratee = function(value, context) {}; //返回函數func(obj){} //調用func時 arguments長度小於2 或 obj 爲空,返回obj //拷貝第二個開始日後參數的屬性值(undefinedOnly不爲真 或 obj無此屬性)給obj var createAssigner = function(keysFunc, undefinedOnly) {}; //實現Object.create() var baseCreate = function(prototype) {}; //返回函數func(obj){} //調用func時 obj不爲空返回 obj[key] var property = function(key) {}; //2的53次方減1 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; //獲取對象的長度 var getLength = property('length'); //經過length屬性 類數組判斷 var isArrayLike = function(collection) {}; _.each = _.forEach = function(obj, iteratee, context) {}; _.map = _.collect = function(obj, iteratee, context) {}; //返回函數function(obj, iteratee, memo, context); //調用返回函數 爲將obj的值以 dir正負表明左右步數 的順序代入iteratee中(memo有值,以momo爲第一個參數),返回最終的結果 function createReduce(dir) {} //從左到右步進1,執行createReduce返回函數 _.reduce = _.foldl = _.inject = createReduce(1); //從右到左步進1,執行createReduce返回函數 _.reduceRight = _.foldr = createReduce(-1); _.find = _.detect = function(obj, predicate, context) {}; _.filter = _.select = function(obj, predicate, context) {}; _.reject = function(obj, predicate, context) {}; _.every = _.all = function(obj, predicate, context) {}; _.some = _.any = function(obj, predicate, context) {}; _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {}; _.invoke = function(obj, method) {}; _.pluck = function(obj, key) {}; _.where = function(obj, attrs) {}; _.findWhere = function(obj, attrs) {}; // Infinity爲無窮大 _.max = function(obj, iteratee, context) {}; _.min = function(obj, iteratee, context) {}; _.shuffle = function(obj) {}; _.sample = function(obj, n, guard) {}; _.sortBy = function(obj, iteratee, context) {}; //內部方法,返回function(obj, iteratee, context)供groupBy、indexBy、countBy調用 var group = function(behavior) {}; _.groupBy = group(function(result, value, key) {}); _.indexBy = group(function(result, value, key) {}); _.countBy = group(function(result, value, key) {}); _.toArray = function(obj) {}; _.size = function(obj) {}; _.partition = function(obj, predicate, context) {}; _.first = _.head = _.take = function(array, n, guard) {}; _.initial = function(array, n, guard) {}; _.last = function(array, n, guard) {}; _.rest = _.tail = _.drop = function(array, n, guard) {}; _.compact = function(array) {}; //內部方法,返回數組供flatten、union、difference、pick調用 var flatten = function(input, shallow, strict, startIndex) {}; _.flatten = function(array, shallow) {}; _.without = function(array) {}; _.uniq = _.unique = function(array, isSorted, iteratee, context) {}; _.union = function() {}; _.intersection = function(array) {}; _.difference = function(array) {}; _.zip = function() {}; _.unzip = function(array) {}; _.object = function(list, values) {}; //內部方法,返回function(obj, iteratee, context) function createPredicateIndexFinder(dir) {} _.findIndex = createPredicateIndexFinder(1); _.findLastIndex = createPredicateIndexFinder(-1); _.sortedIndex = function(array, obj, iteratee, context) {}; function createIndexFinder(dir, predicateFind, sortedIndex) {} _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); _.range = function(start, stop, step) {}; var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {}; _.bind = function(func, context) {}; _.partial = function(func) {}; _.bindAll = function(obj) {}; _.memoize = function(func, hasher) {}; _.delay = function(func, wait) {}; _.defer = _.partial(_.delay, _, 1); _.throttle = function(func, wait, options) {}; }; _.debounce = function(func, wait, immediate) {}; _.wrap = function(func, wrapper) {}; _.negate = function(predicate) {}; _.compose = function() {}; _.after = function(times, func) {}; _.before = function(times, func) {}; _.once = _.partial(_.before, 2); var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; function collectNonEnumProps(obj, keys) {} _.keys = function(obj) {}; _.allKeys = function(obj) {}; _.values = function(obj) {}; _.mapObject = function(obj, iteratee, context) {}; _.pairs = function(obj) {}; _.invert = function(obj) {}; _.functions = _.methods = function(obj) {}; _.extend = createAssigner(_.allKeys); _.extendOwn = _.assign = createAssigner(_.keys); _.findKey = function(obj, predicate, context) {}; _.pick = function(object, oiteratee, context) {}; _.omit = function(obj, iteratee, context) {}; _.defaults = createAssigner(_.allKeys, true); _.create = function(prototype, props) {}; _.clone = function(obj) {}; _.tap = function(obj, interceptor) {}; _.isMatch = function(object, attrs) {}; //+a爲parseInt(a);遞歸判斷引用類型 var eq = function(a, b, aStack, bStack) {}; _.isEqual = function(a, b) {}; _.isEmpty = function(obj) {}; _.isElement = function(obj) {}; _.isArray = nativeIsArray || function(obj) {}; _.isObject = function(obj) {}; //批量添加類型判斷的方法 _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; }); //arguments.callee當前正在執行函數,嚴格模式禁止使用 if (!_.isArguments(arguments)) {} //typeof /./爲object; typeof Int8Array爲function if (typeof /./ != 'function' && typeof Int8Array != 'object') { _.isFunction = function(obj) { return typeof obj == 'function' || false; }; } // isFinite(obj)檢查obj是不是無窮大。 _.isFinite = function(obj) {}; _.isNaN = function(obj) {}; _.isBoolean = function(obj) {}; _.isNull = function(obj) {}; _.isUndefined = function(obj) {}; _.has = function(obj, key) {}; //原root._指向_;root._ = previousUnderscore將root._指向頁面原有的_; //this指向_ _.noConflict = function() {}; _.identity = function(value) {}; _.constant = function(value) {}; _.noop = function(){}; _.property = property; _.propertyOf = function(obj) {}; _.matcher = _.matches = function(attrs) { }; _.times = function(n, iteratee, context) {}; _.random = function(min, max) {}; _.now = Date.now || function() {}; var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var unescapeMap = _.invert(escapeMap); var createEscaper = function(map) {}; _.escape = createEscaper(escapeMap); _.unescape = createEscaper(unescapeMap); _.result = function(object, property, fallback) {}; var idCounter = 0; _.uniqueId = function(prefix) {}; //模板相關 _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; var noMatch = /(.)^/; var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\u2028|\u2029/g; var escapeChar = function(match) { return '\\' + escapes[match]; }; _.template = function(text, settings, oldSettings) {}; //鏈式調用 _.chain = function(obj) {}; // 中間函數,是否鏈式調用 var result = function(instance, obj) { return instance._chain ? _(obj).chain() : obj; }; // 將obj上面的函數表達式所有掛載到_的原型上 _.mixin = function(obj) {}; //將_上面的函數表達式所有掛載到_的原型上 _.mixin(_); // 將數組方法掛載到_的原型上(原數組改變) _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; return result(this, obj); }; }); //將數組方法掛載到_的原型上(原數組不變) _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result(this, method.apply(this._wrapped, arguments)); }; }); _.prototype.value = function() {}; _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; _.prototype.toString = function() {}; if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } }.call(this));
一、最經常使用的調用方式_.method(arguments)
示例1:編程
var arr = [1, 2, 3]; // 最經常使用,類函數式編程 var test1=_.map(arr, function(num) { return num; });
調用流程主要代碼:數組
var root = this; //_函數表達式聲明 var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; //將_函數賦值給調用的exports._或 root._(根據環境) if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } //聲明不一樣的方法做爲_的屬性值 _.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函數。此方法比較簡單易懂app
二、OOP風格,類jquery調用_(arguments1).method(arguments2)
示例2:dom
var arr = [1, 2, 3]; // OOP風格,類jquery調用 var test2=_(arr).map(function(num) { return num* 3; });
示例3:鏈式調用curl
var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}]; var test3 = _.chain(stooges) .sortBy(function(stooge){ return stooge.age; }) .map(function(stooge){ return stooge.name + ' is ' + stooge.age; }) .first() .value(); console.log("test3"); console.log(test3);
調用流程主要代碼:ide
var _ = function(obj) { //判斷obj是不是_的實例,是返回實例,不是下一步; if (obj instanceof _) return obj; //判斷this是否爲_的實例,是把obj賦於this實例的變量_wrapped //不是返回一個新實例,執行這個新實例時,新實例裏面的this指向該新實例。 if (!(this instanceof _)) return new _(obj); //把obj賦於新實例的變量_wrapped this._wrapped = obj; }; _.chain = function(obj) { //判斷obj是不是_的實例,是返回該實例,不是生成一個新實例;將實例賦值給instance var instance = _(obj); //鏈式調用標記 instance._chain = true; //返回實例 return instance; }; var result = function(instance, obj) { //是否繼續鏈式調用 return instance._chain ? _(obj).chain() : obj; }; _.mixin = function(obj) { //_.functions(obj)返回obj上值類型爲function的key的集合 _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { //如示例2,this._wrapped爲arr var args = [this._wrapped]; //如示例2,args變成[arr,function(num) {return num* 3;}] push.apply(args, arguments); //如示例2,返回_.map.apply(_,[arr,function(num) {return num* 3;}]) return result(this, func.apply(_, args)); }; }); }; //將_傳入_.mixin _.mixin(_); //獲取鏈式調用的結果 _.prototype.value = function() { return this._wrapped; };