// Underscore.js 1.3.3// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.// Underscore is freely distributable under the MIT license.// Portions of Underscore are inspired or borrowed from Prototype,// Oliver Steele's Functional, and John Resig's Micro-Templating.// For all details and documentation:// http://documentcloud.github.com/underscore(function() { // 創建一個全局對象, 在瀏覽器中表示為window對象, 在Node.js中表示global對象 var root = this; // 保存"_"(下劃線變量)被覆蓋之前的值 // 如果出現命名沖突或考慮到規范, 可通過_.noConflict()方法恢復"_"被Underscore占用之前的值, 并返回Underscore對象以便重新命名 var previousUnderscore = root._; // 創建一個空的對象常量, 便于內部共享使用 var breaker = {}; // 將內置對象的原型鏈緩存在局部變量, 方便快速調用 var ArrayProto = Array.prototype, // ObjProto = Object.prototype, // FuncProto = Function.prototype; // 將內置對象原型中的常用方法緩存在局部變量, 方便快速調用 var slice = ArrayProto.slice, // unshift = ArrayProto.unshift, // toString = ObjProto.toString, // hasOwnProperty = ObjProto.hasOwnProperty; // 這里定義了一些JavaScript 1.6提供的新方法 // 如果宿主環境中支持這些方法則優先調用, 如果宿主環境中沒有提供, 則會由Underscore實現 var nativeForEach = ArrayProto.forEach, // nativeMap = ArrayProto.map, // nativeReduce = ArrayProto.reduce, // nativeReduceRight = ArrayProto.reduceRight, // nativeFilter = ArrayProto.filter, // nativeEvery = ArrayProto.every, // nativeSome = ArrayProto.some, // nativeIndexOf = ArrayProto.indexOf, // nativeLastIndexOf = ArrayProto.lastIndexOf, // nativeIsArray = Array.isArray, // nativeKeys = Object.keys, // nativeBind = FuncProto.bind; // 創建對象式的調用方式, 將返回一個Underscore包裝器, 包裝器對象的原型中包含Underscore所有方法(類似與將DOM對象包裝為一個jQuery對象) var _ = function(obj) { // 所有Underscore對象在內部均通過wrapper對象進行構造 return new wrapper(obj); }; // 針對不同的宿主環境, 將Undersocre的命名變量存放到不同的對象中 if( typeof exports !== 'undefined') {// Node.js環境 if( typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else {// 瀏覽器環境中Underscore的命名變量被掛在window對象中 root['_'] = _; } // 版本聲明 _.VERSION = '1.3.3'; // 集合相關的方法(數據和對象的通用處理方法) // -------------------- // 迭代處理器, 對集合中每一個元素執行處理器方法 var each = _.each = _.forEach = function(obj, iterator, context) { // 不處理空值 if(obj == null) return; if(nativeForEach && obj.forEach === nativeForEach) { // 如果宿主環境支持, 則優先調用JavaScript 1.6提供的forEach方法 obj.forEach(iterator, context); } else if(obj.length === +obj.length) { // 對<數組>中每一個元素執行處理器方法 for(var i = 0, l = obj.length; i < l; i++) { if( i in obj && iterator.call(context, obj[i], i, obj) === breaker) return; } } else { // 對<對象>中每一個元素執行處理器方法 for(var key in obj) { if(_.has(obj, key)) { if(iterator.call(context, obj[key], key, obj) === breaker) return; } } } }; // 迭代處理器, 與each方法的差異在于map會存儲每次迭代的返回值, 并作為一個新的數組返回 _.map = _.collect = function(obj, iterator, context) { // 用于存放返回值的數組 var results = []; if(obj == null) return results; // 優先調用宿主環境提供的map方法 if(nativeMap && obj.map === nativeMap) return obj.map(iterator, context); // 迭代處理集合中的元素 each(obj, function(value, index, list) { // 將每次迭代處理的返回值存儲到results數組 results[results.length] = iterator.call(context, value, index, list); }); // 返回處理結果 if(obj.length === +obj.length) results.length = obj.length; return results; }; // 將集合中每個元素放入迭代處理器, 并將本次迭代的返回值作為"memo"傳遞到下一次迭代, 一般用于累計結果或連接數據 _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { // 通過參數數量檢查是否存在初始值 var initial = arguments.length > 2; if(obj == null) obj = []; // 優先調用宿主環境提供的reduce方法 if(nativeReduce && obj.reduce === nativeReduce && false) { if(context) iterator = _.bind(iterator, context); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); } // 迭代處理集合中的元素 each(obj, function(value, index, list) { if(!initial) { // 如果沒有初始值, 則將第一個元素作為初始值; 如果被處理的是對象集合, 則默認值為第一個屬性的值 memo = value; initial = true; } else { // 記錄處理結果, 并將結果傳遞給下一次迭代 memo = iterator.call(context, memo, value, index, list); } }); if(!initial) throw new TypeError('Reduce of empty array with no initial value'); return memo; }; // 與reduce作用相似, 將逆向迭代集合中的元素(即從最后一個元素開始直到第一個元素) _.reduceRight = _.foldr = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if(obj == null) obj = []; // 優先調用宿主環境提供的reduceRight方法 if(nativeReduceRight && obj.reduceRight === nativeReduceRight) { if(context) iterator = _.bind(iterator, context); return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); } // 逆轉集合中的元素順序 var reversed = _.toArray(obj).reverse(); if(context && !initial) iterator = _.bind(iterator, context); // 通過reduce方法處理數據 return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator); }; // 遍歷集合中的元素, 返回第一個能夠通過處理器驗證的元素 _.find = _.detect = function(obj, iterator, context) { // result存放第一個能夠通過驗證的元素 var result; // 通過any方法遍歷數據, 并記錄通過驗證的元素 // (如果是在迭代中檢查處理器返回狀態, 這里使用each方法會更合適) any(obj, function(value, index, list) { // 如果處理器返回的結果被轉換為Boolean類型后值為true, 則當前記錄并返回當前元素 if(iterator.call(context, value, index, list)) { result = value; return true; } }); return result; }; // 與find方法作用類似, 但filter方法會記錄下集合中所有通過驗證的元素 _.filter = _.select = function(obj, iterator, context) { // 用于存儲通過驗證的元素數組 var results = []; if(obj == null) return results; // 優先調用宿主環境提供的filter方法 if(nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); // 迭代集合中的元素, 并將通過處理器驗證的元素放到數組中并返回 each(obj, function(value, index, list) { if(iterator.call(context, value, index, list)) results[results.length] = value; }); return results; }; // 與filter方法作用相反, 即返回沒有通過處理器驗證的元素列表 _.reject = function(obj, iterator, context) { var results = []; if(obj == null) return results; each(obj, function(value, index, list) { if(!iterator.call(context, value, index, list)) results[results.length] = value; }); return results; }; // 如果集合中所有元素均能通過處理器驗證, 則返回true _.every = _.all = function(obj, iterator, context) { var result = true; if(obj == null) return result; // 優先調用宿主環境提供的every方法 if(nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); // 迭代集合中的元素 each(obj, function(value, index, list) { // 這里理解為 result = (result && iterator.call(context, value, index, list)) // 驗證處理器的結果被轉換為Boolean類型后是否為true值 if(!( result = result && iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // 檢查集合中任何一個元素在被轉換為Boolean類型時, 是否為true值?或者通過處理器處理后, 是否值為true? var any = _.some = _.any = function(obj, iterator, context) { // 如果沒有指定處理器參數, 則默認的處理器函數會返回元素本身, 并在迭代時通過將元素轉換為Boolean類型來判斷是否為true值 iterator || ( iterator = _.identity); var result = false; if(obj == null) return result; // 優先調用宿主環境提供的some方法 if(nativeSome && obj.some === nativeSome) return obj.some(iterator, context); // 迭代集合中的元素 each(obj, function(value, index, list) { if(result || ( result = iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // 檢查集合中是否有值與目標參數完全匹配(同時將匹配數據類型) _.include = _.contains = function(obj, target) { var found = false; if(obj == null) return found; // 優先調用宿主環境提供的Array.prototype.indexOf方法 if(nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; // 通過any方法迭代集合中的元素, 驗證元素的值和類型與目標是否完全匹配 found = any(obj, function(value) { return value === target; }); return found; }; // 依次調用集合中所有元素的同名方法, 從第3個參數開始, 將被以此傳入到元素的調用方法中 // 返回一個數組, 存儲了所有方法的處理結果 _.invoke = function(obj, method) { // 調用同名方法時傳遞的參數(從第3個參數開始) var args = slice.call(arguments, 2); // 依次調用每個元素的方法, 并將結果放入數組中返回 return _.map(obj, function(value) { return (_.isFunction(method) ? method || value : value[method]).apply(value, args); }); }; // 遍歷一個由對象列表組成的數組, 并返回每個對象中的指定屬性的值列表 _.pluck = function(obj, key) { // 如果某一個對象中不存在該屬性, 則返回undefined return _.map(obj, function(value) { return value[key]; }); }; // 返回集合中的最大值, 如果不存在可比較的值, 則返回undefined _.max = function(obj, iterator, context) { // 如果集合是一個數組, 且沒有使用處理器, 則使用Math.max獲取最大值 // 一般會是在一個數組存儲了一系列Number類型的數據 if(!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj); // 對于空值, 直接返回負無窮大 if(!iterator && _.isEmpty(obj)) return -Infinity; // 一個臨時的對象, computed用于在比較過程中存儲最大值(臨時的) var result = { computed : -Infinity }; // 迭代集合中的元素 each(obj, function(value, index, list) { // 如果指定了處理器參數, 則比較的數據為處理器返回的值, 否則直接使用each遍歷時的默認值 var computed = iterator ? iterator.call(context, value, index, list) : value; // 如果比較值相比上一個值要大, 則將當前值放入result.value computed >= result.computed && ( result = { value : value, computed : computed }); }); // 返回最大值 return result.value; }; // 返回集合中的最小值, 處理過程與max方法一致 _.min = function(obj, iterator, context) { if(!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj); if(!iterator && _.isEmpty(obj)) return Infinity; var result = { computed : Infinity }; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && ( result = { value : value, computed : computed }); }); return result.value; }; // 通過隨機數, 讓數組無須排列 _.shuffle = function(obj) { // shuffled變量存儲處理過程及最終的結果數據 var shuffled = [], rand; // 迭代集合中的元素 each(obj, function(value, index, list) { // 生成一個隨機數, 隨機數在<0-當前已處理的數量>之間 rand = Math.floor(Math.random() * (index + 1)); // 將已經隨機得到的元素放到shuffled數組末尾 shuffled[index] = shuffled[rand]; // 在前面得到的隨機數的位置插入最新值 shuffled[rand] = value; }); // 返回一個數組, 該數組中存儲了經過隨機混排的集合元素 return shuffled; }; // 對集合中元素, 按照特定的字段或值進行排列 // 相比Array.prototype.sort方法, sortBy方法支持對對象排序 _.sortBy = function(obj, val, context) { // val應該是對象的一個屬性, 或一個處理器函數, 如果是一個處理器, 則應該返回需要進行比較的數據 var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; // 調用順序: _.pluck(_.map().sort()); // 調用_.map()方法遍歷集合, 并將集合中的元素放到value節點, 將元素中需要進行比較的數據放到criteria屬性中 // 調用sort()方法將集合中的元素按照criteria屬性中的數據進行順序排序 // 調用pluck獲取排序后的對象集合并返回 return _.pluck(_.map(obj, function(value, index, list) { return { value : value, criteria : iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; if(a === void 0) return 1; if(b === void 0) return -1; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; // 將集合中的元素, 按處理器返回的key分為多個數組 _.groupBy = function(obj, val) { var result = {}; // val將被轉換為進行分組的處理器函數, 如果val不是一個Function類型的數據, 則將被作為篩選元素時的key值 var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; // 迭代集合中的元素 each(obj, function(value, index) { // 將處理器的返回值作為key, 并將相同的key元素放到一個新的數組 var key = iterator(value, index); (result[key] || (result[key] = [])).push(value); }); // 返回已分組的數據 return result; }; _.sortedIndex = function(array, obj, iterator) { iterator || ( iterator = _.identity); var low = 0, high = array.length; while(low < high) { var mid = (low + high) >> 1; iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; } return low; }; // 將一個集合轉換一個數組并返回 // 一般用于將arguments轉換為數組, 或將對象無序集合轉換為數據形式的有序集合 _.toArray = function(obj) { if(!obj) return []; if(_.isArray(obj)) return slice.call(obj); // 將arguments轉換為數組 if(_.isArguments(obj)) return slice.call(obj); if(obj.toArray && _.isFunction(obj.toArray)) return obj.toArray(); // 將對象轉換為數組, 數組中包含對象中所有屬性的值列表(不包含對象原型鏈中的屬性) return _.values(obj); }; // 計算集合中元素的數量 _.size = function(obj) { // 如果集合是一個數組, 則計算數組元素數量 // 如果集合是一個對象, 則計算對象中的屬性數量(不包含對象原型鏈中的屬性) return _.isArray(obj) ? obj.length : _.keys(obj).length; }; // 數組相關的方法 // --------------- // 返回一個數組的第一個或亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb 日韩欧美国产激情| 国产欧美一区二区三区视频| 一色桃子一区二区| 最近2019免费中文字幕视频三| 久久偷看各类女兵18女厕嘘嘘| 亚洲欧美国产一本综合首页| 亚洲韩国日本中文字幕| 日韩经典中文字幕在线观看| 精品视频久久久久久久| 国产成人精品a视频一区www| 2019中文字幕免费视频| 日本精品久久久久影院| 欧美性xxxx极品hd欧美风情| 久久久亚洲福利精品午夜| 日韩视频中文字幕| 成人黄色生活片| 亚洲一区美女视频在线观看免费| 国产精品福利观看| 久久久成人的性感天堂| 亚洲va久久久噜噜噜久久天堂| 欧美丰满少妇xxxxx| 亚洲精品www久久久久久广东| 日韩天堂在线视频| 亚洲综合日韩在线| 欧美电影免费观看| 清纯唯美日韩制服另类| 国产精品免费久久久久久| 日韩欧美亚洲国产一区| 中文字幕亚洲欧美| 欧美激情免费在线| 高清日韩电视剧大全免费播放在线观看| 国产午夜精品麻豆| 欧美午夜性色大片在线观看| 国产一区红桃视频| 91国内精品久久| 欧美黄色性视频| 国产欧美欧洲在线观看| 久久人人看视频| 亚洲网址你懂得| 国产精品狼人色视频一区| 国产极品精品在线观看| 国产成人精品日本亚洲专区61| 日韩国产中文字幕| 2019日本中文字幕| 亚洲第一精品福利| 久久精品国产69国产精品亚洲| 在线看福利67194| 亚洲人午夜色婷婷| 亚洲精品99久久久久| 人人澡人人澡人人看欧美| 精品久久久在线观看| 欧美美最猛性xxxxxx| 在线播放精品一区二区三区| www.久久久久久.com| 亚洲级视频在线观看免费1级| 欧美性猛交xxxx富婆弯腰| 欧美激情精品久久久久| 久久久久国产视频| 国产噜噜噜噜噜久久久久久久久| 亚洲国产高清福利视频| 亚洲欧美一区二区三区在线| 丝袜情趣国产精品| 中日韩美女免费视频网站在线观看| 亚洲美女av黄| 欧美电影免费观看大全| 一区二区三区日韩在线| 成人网址在线观看| 久久97精品久久久久久久不卡| 国产精品影片在线观看| 97色在线视频观看| 中文字幕亚洲欧美| 久久最新资源网| 91高潮在线观看| 欧美剧在线观看| 91社区国产高清| 国产精品大陆在线观看| 亚洲永久免费观看| 色哟哟入口国产精品| 欧美成人精品三级在线观看| 成人精品视频久久久久| 日韩精品在线看| 亚洲第一视频在线观看| 国产成人亚洲精品| 在线不卡国产精品| 日韩电影第一页| 国产精品私拍pans大尺度在线| 91系列在线播放| 91午夜在线播放| 欧美成人国产va精品日本一级| 91亚洲精品在线观看| 亚洲国产欧美一区| 91久久久在线| 精品色蜜蜜精品视频在线观看| 97精品伊人久久久大香线蕉| 亚洲人成网站777色婷婷| 国产精品夫妻激情| 日韩欧美主播在线| 成人激情视频小说免费下载| 欧美电影在线观看高清| 国产精品久久中文| 九九热精品视频| 国产欧美精品xxxx另类| 国产一区二区三区视频| 姬川优奈aav一区二区| 亚洲aⅴ男人的天堂在线观看| 欧美成人激情视频| 精品国产乱码久久久久久婷婷| 一区二区成人av| 日韩av中文字幕在线播放| 亚洲精品99久久久久| 午夜精品久久久久久久久久久久| 91av在线不卡| 狠狠综合久久av一区二区小说| 欧美激情视频网站| 久久久久久久久久久免费精品| 亚洲在线一区二区| 亚洲人成在线免费观看| 亚洲欧美另类在线观看| 亚洲xxxx视频| 91久久精品国产91性色| 日韩免费观看在线观看| 欧美多人乱p欧美4p久久| 日韩精品丝袜在线| 日韩美女中文字幕| 在线看国产精品| 亚洲国产高清高潮精品美女| 国产精品成人观看视频国产奇米| 国产va免费精品高清在线观看| 精品中文字幕在线观看| 欧美成人亚洲成人日韩成人| 国产999精品久久久| 国产精品av在线播放| 97国产suv精品一区二区62| 国产精品美女午夜av| 久久视频在线播放| 欧美一性一乱一交一视频| 国产精品亚洲激情| 亚洲欧美国产日韩天堂区| xxxx欧美18另类的高清| 欧美一性一乱一交一视频| 亚洲欧洲第一视频| 在线观看日韩www视频免费| 久久久久久久激情视频| 国产亚洲在线播放| 国产精品久久久久久av福利| 国产不卡av在线免费观看| 欧美丰满少妇xxxxx做受| 成人免费福利在线| 国产精品手机播放| 国产成人在线播放| 国产成人亚洲精品| 大桥未久av一区二区三区| 亚洲国产精品国自产拍av秋霞| 国语自产精品视频在线看一大j8| 亚洲欧美日韩一区二区三区在线| 国产精品第一页在线| 日本精品视频网站| 69视频在线免费观看| 久久香蕉国产线看观看网| 国产成人91久久精品| 国产精品wwww| 亚洲性日韩精品一区二区| 国产美女高潮久久白浆| 日韩中文字幕视频在线|