underscore就是一些函數的集合,大概分爲6部分,分別是基礎函數,集合,數組,函數,對象,實用功能.
在實現每個功能的時候,基本上先本身實現,再對比。
(function(){
}.call(this));
基本框架長這樣,就一個函數的自調用,用window做爲調用對象,防止污染全局。
//把window對象先存起來備用吧。
var root = this;
//弄個window的屬性_;
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是_實例的話,直接返回obj.如果直接調用構造函數而不是經過new操做符的話,會調用第二個if.執行new _(obj).
//另外,這樣的話obj就不是實例了,因此弄個實例屬性_wrapped把obj存起來備用。
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
//這個是什麼nodejs接口的,不懂。過了,嘿嘿
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; }
//這個是關於函數優化的,參數函數,調用EC,參數數量。
//只傳了一個參數的話,context==null.這裏應該是能夠用context === void 0或者context==null,一個意思。直接返回傳入的函數
//沒傳第三個參數的話,默認就是傳三個參數給調用的函數,通常是用在forEach,filter這些數組方法,function(element,index,array);
//第三個參數是1或2的話,感受沒什麼特別的,直接調用吧.是4的話就是reduce函數之類的。
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 2: return function(value, other) {
return func.call(context, value, other);
};
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);
};
};
//沒傳參數的話,直接返回一個函數,設置a,調用這個函數a,返回傳給a的參數
//value是函數的話,直接調用上面的函數優化公式,
//對象的話,返回是否對象匹配的函數。
//返回一個property函數,這個函數調用傳一個對象,value是鍵,返回值
var cb = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value)) return _.matcher(value);
return _.property(value);
};
_.identity = function(value) {
return value;
};
_.property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
//源碼中沒有用到。返回一個迭代函數吧。
_.iteratee = function(value, context) {
return cb(value, context, Infinity);
};
//建立一個對象合併器.keyfunc是一個對象鍵數組函數,返回對象全部鍵。
//undefinedOnly麼是定義是否是隻有往目標對象上添加屬性而無論屬性是否存在。通常爲
//true。也就是當對象不存在這個屬性的時候才添加。
//當返回的函數只有一個參數時或者直接調用,直接返回那個參數。
//不然把對象的參數一個個遍歷出來,添加到新對象,再返回新對象
//放個參考的
//https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
var createAssigner = function(keysFunc, undefinedOnly) {
return function(obj) {
var length = arguments.length;
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
};
//建立一個對象,原型是參數prototype
//參數不是對象的話,直接返回
//有原生的Object.create方法的話直接用
//利用Ctor構造函數建立對象原型。
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
//數組索引的最大值
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
//是否是類數組,包括原生數組和nodelist類型
var isArrayLike = function(collection) {
var length = collection && collection.length;
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
//前面一些基礎函數寫完了。寫正式的了。首先集合方法。
//對數組和對象的鍵集合進行迭代遍歷.每一個key調用函數,返回原來的obj
_.each = _.forEach = function(obj, iteratee, context) {
//iteratee默認3個參數
iteratee = optimizeCb(iteratee, context);
var i, length;
//數組直接遍歷,對象遍歷鍵集合
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
//每一個key調用函數,返回調用後集合數組
_.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;
};
//參數一大堆先不看了。黑
function createReduce(dir) {
function iterator(obj, iteratee, memo, keys, index, length) {
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
}
return function(obj, iteratee, memo, context) {
iteratee = optimizeCb(iteratee, context, 4);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (arguments.length < 3) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
return iterator(obj, iteratee, memo, keys, index, length);
};
}
_.reduce = _.foldl = _.inject = createReduce(1);
_.reduceRight = _.foldr = createReduce(-1);
//predicate爲一個函數,遍歷數組,若return true直接返回。裏面用到一個 _.findIndex函數,先研究下。研究在下面。
//_.findKey也差很少。最終函數返回obj[key];
_.find = _.detect = function(obj, predicate, context) {
var key;
if (isArrayLike(obj)) {
key = _.findIndex(obj, predicate, context);
} else {
key = _.findKey(obj, predicate, context);
}
if (key !== void 0 && key !== -1) return obj[key];
};
//這個函數就是index搜索器,dir表示正者搜仍是反着搜,搜到predicate函數返回的是ture的時候就把索引返回的,都沒搜到就返回-1;
function createIndexFinder(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = array != null && array.length;
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
if (predicate(array[index], index, array)) return index;
}
return -1;
};
}
_.findIndex = createIndexFinder(1);
//和前面那個差很少,不過返回鍵名
_.findKey = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = _.keys(obj), key;
for (var i = 0, length = keys.length; i < length; i++) {
key = keys[i];
if (predicate(obj[key], key, obj)) return key;
}
};
//把predicate返回爲true的堆到一個新數組,再返回這個新數組
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
//擴展本身,利用attrs類型的json,調用傳入一個對象
_.matcher = _.matches = function(attrs) {
attrs = _.extendOwn({}, attrs);
return function(obj) {
return _.isMatch(obj, attrs);
};
};
//傳對象和attrs,attrs相似{"a":1,"b":2}這種json的東西,若json相似的存在於obj中,則返回true,不然false;
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
//遍歷obj中的每個值,返回一個數組,這個數組包含properties所列出的屬性的全部的鍵值對。
//這裏的obj是類[{},{},{}]的東西
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
//所有obj的元素分別調用predicate,有一個返回值是false,整個返回false//不然返回true_.every = _.all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true;};//所有obj的元素分別調用predicate,有一個返回值是true,整個返回false//不然返回false_.some = _.any = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false;};//檢查obj是否包含target,就是indexOf方法的調用。_.contains = _.includes = _.include = function(obj, target, fromIndex) { if (!isArrayLike(obj)) obj = _.values(obj); return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;};//對每一個obj元素調用method,注意method方法的獲取,例子用了[]['sort'],獲取到sort函數引用//args,是傳給method的參數,可選。_.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; return func == null ? func : func.apply(value, args); });};_.invoke([[5, 1, 7], [3, 2, 1]], 'sort');//傳key,得到值組成的數組。_.pluck = function(obj, key) { return _.map(obj, _.property(key));};var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];_.pluck(stooges, 'name');//結果["moe", "larry", "curly"]//普通的數組且沒有傳iteratee的狀況下,直接用交換機的方式比較最大值//傳了迭代函數的話,通常obj是[{a:1},{a:2},{a:3}],迭代函數把各個對象的相應鍵的值取出來比較。再返回最大值_.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = cb(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result;};//用 Fisher-Yates shuffle算法,產生隨機排序數組//https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle_.shuffle = function(obj) { var set = isArrayLike(obj) ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); //這裏用交換機,rand<index的時候,把shuffled【rand】索引的值放到shuffled【index】索引上 if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled;};//guard默認是1,n==null的時候,默認隨機返回一個obj數組元素//n有值的時候,返回n個元素_.sample = function(obj, n, guard) { if (n == null || guard) { if (!isArrayLike(obj)) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n));};//通常用來轉換nodelist爲真正的數組,能夠調用原生的數組方法。_.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj);};//數組的長度或者對象鍵集合的長度。_.size = function(obj) { if (obj == null) return 0; return isArrayLike(obj) ? obj.length : _.keys(obj).length;};//obj每一個元素分別調用predicate,返回true的扔到pass數組,false的扔到fail數組_.partition = function(obj, predicate, context) { predicate = cb(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail];};集合的函數差很少就這些,其實還有4個函數,以爲用不太到還挺難就不寫了,哈哈哈哈哈