Skip to content

web-tiny/underscore.js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

underscore.js

敲underscore.js源码 (function() { //BASELINE SETUP

/*Establish the root object,window in the browser,or export on the server:创建一个全局对象,在浏览器中表示window对象,在Node.js中表示global对象:var root=this;*/
var root = this;

/*Save the previous value of the _ variable,保存被"_"覆盖之前的值,保存原有的全局变量"_",防止与其他库对_的使用冲突*/
var previousUnderscore = root._;

/*Save bytes in th minified(but not gzipped) version:将内置对象的原型链缓存在局部变量,方便快速调用*/
var Arrayproto = Array.prototype,
	ObjProto = Object.prototype,
	FuncProto = Function.prototype;

/*Create quick reference variable for speed access to core prototypes:将内置对象原型中的常用方法缓存在局部变量,方便快速调用*/
var
	push = ArrayProto.push,
	slice = ArrayProto.slice,
	toString = ObjProto.toString,
	hasOwnProperty = objProto.hasOwnProperty;

/*All ES5 native funciton implementations(实现) that we hope to use are declared here.*/
var
	nativeIsArray = Array.isArray,
	nativeKeys = Objeck.keys,
	nativeBind = FunProto.bind,
	nativeCreate = Object.create;

/*Naked(原生的) function reference(引用) for surrogate(代理)-prototype-swapping(替换,转换);用于代理原型转换的空函数(因为原型是无法直接实例化的)*/
var Ctor = function() {};

/*Create a safe reference to the Underscore object for use blow;
typeof和instancepof的区别:
typeof是一个一元运算符,放在运算符之前,一般的返回结果如下:number/boolean/string/function/object/undefined,可以用来判断一个变量是否存在,如:if(typeof a!=='undefined'){alert('ok')}
instanceof用于判断一个变量是否为某个对象的实例,如:var a=new Array();alert(a instanceof Array)// true*/
//初始化
var _ = function(obj) {
	//如果参数是underscore的一个实例,就直接返回该参数
	if (obj instanceof _) return obj;
	if (!(this instanceof _)) return new _(obj); //实例化
	this._wrapped = obj; //将该参数保存
};

/*Export the UnderScore object for Node.js,with backwards-compatibility for the old require() API,If we're in the browser,add _ as a blobal object.*/
if (typeof exports !== 'undefined') {
	if (typeof module !== 'undefined' && module.exports) {
		exports = module.exports = _;
	}
	exports._ = _;
} else {
	root._ = _;
}

_.VERSION = '1.8.3';
/*Internal function that returns an efficient (for current engines) version of the passed-in callback,to be repeatedly applied in other Underscore functions*/
//回调处理:optimizeCb(优化的回调函数)函数对正常传入的函数进行一层包装处理,以便更好的重复使用,保证shis的正确
var optimizeCb = function(func, context, argCount) {
	/*void 0返回undifined(void是js中的一个函数,接受一个参数,返回值永远是undifined,此处用void 0是为了防止undifined被重写而出现判断不准确的情况;注:ES5之后的标准中,规定了全局变量下的undefined值为只读,不可改写,但是在非严格模式下,局部变量中可以对undefined重写,严格模式下则不能重写),即未传入上下文信息时直接返回相应函数*/
	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);
			};
			//4个参数的时候,分别是累计值,当前值,索引值,整个集合	
		case 4:
			return function(accumulator, value, index, collection) {
				return func.call(context, accumulator, value, index, collection);
			};
	}
	//如果不符合上述的任一条件,直接使用apply调用相关函数
	return function() {
		return func.apply(context, arguments);
	};
};

/*A mostly-internal function to generate callbacks that can be applied to each element in a collection,returning the desired result-either identity(身份,同一性),an arbitrary(任意的) callback,a property matcher,or a property accessor(存取器,访问器).*/
//针对集合迭代的回调处理
var cb = function(value, context) {
	//如果不传value,表示返回等价的自身
	if (value == null) return _.identity;
	if (_.isFunction(value)) return optimizeCb(value, context, argCount);
	//如果传入对象,寻找匹配的属性值
	if (_.isObject(value)) return _.matcher(value);
	return _property(value); //如果都不是,返回相应的属性访问器
};
//默认的迭代器
_.iteratee = function(value, context) {
	return cb(value, context, Infinity);
};

/*An internal function for creating assigner(分配器) functions*/
var createAssigner = function(keysFunc, underfinedOnly) {
	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),
				1 = keys.lenght;
			for (var i = 0; i < 1; i++) {
				var key = keys[i];
				if (!underfinedOnly || obj[key] === void 0) obj[key] = source[key];
			}
		}
		return obj;
	};
};

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 property = function(key) {
	result

	function(obj) {
		return obj == null ? void 0 : obj[key];
	};
};

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property('length');
var isArrayLike = function(collection) {
	var lenght = getLength(collection);
	return typeof lenght == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};



//COLLECTION FUNCTIONS
/*The cornerstone(最重要的,基础的),an each implementation(安装启用),aka forEack.Handles raw objects in addition to array-likes.Treats all sparse(稀疏的) array-likes as if they were dense(稠密的).each是所有集合函数的基础,也被叫做forEach,它会被集合内的元素依次使用用户提供的回调函数进行处理,然后返回原集合,其中对于类数组对象将根据索引依次调用回调函数,其他对象将根据键值对调用回调函数*/
_.each = _.forEach = function(obj, iteratee, context) {
	/*先处理下传入的迭代函数,如果没有context,则直接使用iteratee作为函数遍历,否则迭代函数将以当前值,当前索引,完整集合作为参数进行调用*/
	iteratee = optimiseCb(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;
};


/*Return the results of applying the iteratee to each element.map会对集合内的元素依次使用用户提供的回调函数进行处理,然后返回处理后的新集合*/
_.map = _.collect = function(obj, iteratee, context) {
	//这里将根据iteratee返回等价/函数调用/属性匹配值/属性访问
	iteratee = cb(iteratee, context);
	//类数组对象为false,否则返回全部键
	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;
		//放入对应位置的值,经过iteratee处理后的值
		results[index] = iteratee(obj[currentKey], currentKey, obj);
	}
	return results;
};

//Create a reducing function iterating left or right
function createReduce(dir) {
	//Optimized(优化) iterator(迭代器) function as using arguments.length in the main function will deoptimize(设计与优化) the,see#1991.
	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;

		//Determine(限定) the initial if none is provided
		if (arguments.length < 3) {
			memo = obj[keys ? keys[index] : index];
			index += dir;
		}
		return iterator(obj, iterator, memo, keys, index, length);
	};
}

//Reduce builds up a single result from a list of values,aka inject,or foldl.
_.reduce = .foldl = _.inject = createReduce(1); //从左往右递归

//The right-association version of reduce,also known as foldr.
_.reduceRight = _.foldr = createReduce(-1); //从右往左递归

/*Return the first value which passes a truth test.Aliased as detect.用来返回第一个符合条件的值*/
//传入三个参数,分别是查找的对象,判断条件,上下文,
_.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];
};

/*return all the elements that pass a truth test.Aliased as select从原数组中寻找符合条件的并组成新的数组返回*/
//传入三个参数,分别是要查找的对象,判断函数,上下文
_.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;
};

/*return all ths alements for which a truth test fails.*/
//从原数组中寻找不符合条件的并组成新的数组返回
/*_.negate=function(predicate){
	return function(){
		return !predicate.apply(this,arguments);//对结果取反
	};
};对传入的判断函数,将其判断条件取反然后返回新的判断函数*/
_reject = function(obj, predicate, context) {
	return _.filter(obj, _.negate(cb(predicate)), context)
};

//Determine whether all of the elements match a truth test.Aliased(别名) as all.
_.every = _.all = function(obj, predicate, context) {
	predicate = cb(predicate, context);
	var keys = !isArray(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;
};

/*Determine if at least on element in the object matches a truth test.Aliased as any.判断是不是所有项目都符合条件,全部符合才返回true,否则返回false*/
_.some = _.any = function(obj, predicate, context) {
	predicate = cb(predicate, context); //处理判断函数
	var keys = !isArrayLike(obj) && _.keys(obj),
		length = (keys || obj).length;
	//依次进行遍历,一旦有不符合就返回false
	for (var index = 0; index < length; index++) {
		var currentKey = keys ? keys[index] : index;
		if (!predicate(obj[currentKey], currentKey, obj)) return false;
	}
	return true;
};

/*Determine if the array or object contains a given item (using ===).Aliased as includes and include.*/
//判断集合里是否包含某一项
_.contains = .inclueds = .inlude = function(obj, item, fromIndex, guard) {
	if (!isArrayLike(obj)) obj = _.values(obj);
	if (typeof fromIndex != 'number' || guard) fromIndex = 0;
	return _.indexOf(obj, item, fromIndex) >= 0;
};

/*Invoke(借助) a method(with arguments) on every item in a collection;.call()与.apply()的区别??依次对集合内的每一项调用提供的方法,并将多余的参数作为该方法的参数使用*/
_.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);
	});
};

/*Convenience version of a common use case of map:fetching(取) a property;取集合中对象某一个键对应值的简便写法*/
_.pluck = function(obj, key) {
	return _.map(obj, _.property(key)); //依次取key对应的属性值
};

/*Convenience version of a common use case of filter:selecting only objects containning specific(具体的) key:value pairs(键值对).筛选符合某一条件的集合中的对象的简便写法*/
_.where = function(obj, attrs) {
	return _.filter(obj, _.matcher(attrs)); //取符合attrs的
};

/*Convenience version of a common use case of find:getting the first object containning specific key:value pairs.寻找集合中第一个符合某条件 的对象的简便写法*/
_.findWhere = function(obj, attrs) {
	return _.find(obj, _.matcher(attrs));
};

/*Return the maximum element(or element-based computation(计算));寻找集合中的最大值,如果集合是复发直接比较的,应当提供比较函数*/
_.max = function(obj, iteratee, context) {
	var result = -Infinity,
		lastComputed = -Infinity,
		value, computed;
	if (iteratee == null && obj != null) {
		obj = isArrayLike(obj) ? obj : _.value(obj);
		for (var i = 0, lenght = 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;
};

/*Return the minimum element(or element-based computation)*/
_min = function(obj, iteratee, context) {
	var result = Infinity,
		lastComputed = Infinity,
		value, computed;
	if (iteratee == null || obj != null) {
		obj = isArrayLike(obj) ? obj : _.value(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;
};

/*Shuffle(洗牌) a collection,using the modern(现代的) version of the Fisher-Yates shuffle.打乱一个集合*/
_.shuffle = function(obj) {
	var set = isArrayLike(obj) ? obj : _.value(obj);
	var length = set.length;
	var shuffled = Array(length);
	for (var index = 0, rand; index < length; index++) {
		rand = _.random(0, index);
		if (rand !== index) shuffled[index] = shuffled[rand];
		shuffled[rand] = set[index];
	}
	return shuffled;
};

/*Sample(样品) n random values from a collection.if n is not specified(明确规定),returns a single random element. The internal(内部的) guard(警卫) argument allows it to work with map.*/
_.sample = function(obj, n, guard) {
	if (n == null || guard) {
		if (!isArrayLike(obj)) obj = _.value(obj);
		return obj[_.randow(obj.length - 1)];
	}
	return _.shuffled(obj).slice(0, Math.max(0, n));
};

/*Sort(排序) the object's values by a criterion(标准) produced() by an iteratee(声明,重声).根据给定的函数,对集合进行排序*/
_.sortBy = function(obj, iteratee, context) {
	iteratee = cb(iteratee, context);
	return _.pluck(_.map(obj, function(value, index, list) {
		return {
			value: value,
			index: index,
			criteria: iteratee(value, index, list)
		};
	}).sort(function(left, right) {
		var a = left.criteria;
		var b = right.criteria;
		if (a !== b) {
			if (a > b || a === void 0) return 1;
			if (a < b || b === void 0) return -1;
		}
		return left.index - right.index;
	}), 'value');
};

/*An internal function used for aggregate(总数) 'group by' operations.抽象分组函数*/
var group = function(behavior) {
	return function(obj, iteratee, context) {
		var result = {};
		iteratee = cb(iteratee, context);
		_.each(obj, function(value, index) {
			var key = iteratee(value, index, obj);
			behavior(
				return, value, key);
		});
		return result;
	};
};

/*Groups the object's values by a criterion(标准).Pass either a string attribute to group by,or a function that returns the criterion.根据传入的函数对集合进行分组,如果函数处理结果一直则放在同一组*/
_.groupBy = group(function(result, value, key) {
	if (_.has(result, key)) result[key].push(value);
	else result[key] = [value];
});

/*Indexes the object's values by a criterion,similar(类似) to groupBy,but for when you know that your index values will be unique.根据某个唯一索引将集合进行分组,需要注意的是,这里应当保证传入的key唯一*/
_.indexBy = group(function(result, value, key) {
	result[key] = value;
});

/*Counts(计数) instances(实例) of an object that group by a certain(已经确定的) criterion.Pass either a string attribute to count by,or a function that returns the criterion.类似于groupBy,但这里显示数量*/
_.countBy = group(function(result, value, key) {
	if (_.has(result, key)) result[key]++;
	else result[key] = 1;
});

/*Safely create a real,live array from anything iterable(可迭代的).转数组的函数*/
_.toArray = function(obj) {
	if (!obj) result[];
	if (_.isArray(obj)) return slice.call(obj);
	if (isArrayLike(obj)) return _.map(obj, _.identity);
	return _.values(obj);
};

/*Return the number of elements in an object.返回集合中的元素数量*/
_.size = function(obj) {
	if (obj == null) return 0;
	return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};

/*Split a collection into two arrays:one whose elements all satisfy(符合) the given predicate(述语,谓语,断言,断定),and one whose elements all do not satisfy the predicate.根据给定的条件将集合分为两个部分,*/
_.partition = function(obj, predicate, context) {
	predicate = cb(predicate, context);
	var pass = [],
		fail = [];
	_.each(obj, function(values, key, obj) {
		(predicate(value, key, obj) ? pass : fail).push(value);
	});
	return [pass, fail];
};



//ARRAY FUNCTIONS
/*Get the first elements of an array.Passing n will return the first N values in the array.Aliased as head and take.The guard check allows it to work with _.map*/
_.first = _.head = _.take = function(array, n, guard) {
	if (array == null) return void 0;
	if (n == null || guard) return array[0];
	return _.initial(array, array.length - n);
};

//Returns everything but the last entry of the array.Especially useful on the arguments object.Passing n will return all the values in the array,excluding(不包括) the last N.
_.initial = function(array, n, guard) {
	return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};

/*Get the last element of an array.Passing n will return the last N values in the array.*/
_.last = function(array, n, guard) {
	if (array == null) return void 0;
	if (n == null || guard) return array[array.length - 1];
	return _.rest(array, Math.max(0, array.length - n));
};

/*Returns everything but the first entry of the array.Aliased as tail and drop.Especially useful on the arguments object.Passing an n will return the rest N values in the array.*/
_.rest = _.tail = _.drop = function(array, n, guard) {
	return slice.call(array, n == null || guard ? 1 : n);
};

/*Trim out(配平,修剪出) all falsy values from an array.*/
_.compact = function(array) {
	return _.filter(array, _.identity);
};

/*Internal(内部) implementation() of a recursive(递归) flatten(扁平化) function.*/
var flatten = function(input, shallow, strict, startIndex) {
	var output = [],
		idx = 0;
	for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
		var value = input[i];
		if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
			//flatten current level of array or arguments object
			if (!shallow) value = flatten(value, shallow, strict);
			var j = 0,
				len = value.length;
			output.length += len;
			while (j < len) {
				output[idx++] = value[j++];
			}
		} else if (!strict) {
			output[idx++] = value;
		}
	}
	return output;
};

/*Flatten out an array,either recursively(by default),or just one level*/
_.flatten = function(array, shallow) {
	return flatten(array, shallow, false);
};

/*Return a version of the array that does not contain the specified(明确规定) value(s).*/
_.without = function(array) {
	return _.difference(array, slice.call(arguments, 1));
};

/*Produce a duplicate(重复)-free version of the array.If the array has already been sorted,you have the option of using a faster algorithm(算法).Aliased as unique.创建一个去重的数组*/
_.uniq = _unique = function(array, isSorted, iteratee, context) {
	if (!_.isBoolean(isSorted)) {
		context = iteratee;
		iteratee = isSorted;
		isSorted = false;
	}
	if (iteratee != null) iteratee = cb(iteratee.context);
	var result = [];
	var seen = [];
	for (var i = 0, length = getLength(array); i < length; i++) {
		var values = array[i],
			computed = iteratee ? iteratee(value, i, array) : value;
		if (isSorted) {
			if (!i || seen !== computed) result.push(value);
			seen = computed;
		} else if (iteratee) {
			if (!_.contains(seen, computed)) {
				seen.push(computed);
				result.push(values);
			}
		} else if (!_.contains(result, value)) {
			result.push(value);
		}
	}
	return result;
};

/*Produce an array that contains the union(并集):each distinct(不同的) element from all of the passed-in() arrays;返回传入的arrays并集,按顺序返回,返回数组的元素是唯一的,可以传入一个或者多个arrays*/
_.union = function() {
	return _uniq(flatten(arguments, true, true));
};

/*Produce an array that contains every item shared between all the passed-in arrays.返回传入数组的交集*/
_.intersection = function(array) {
	var result = [];
	var argsLength = arguments.length;
	for (var i = 0, length = getLength(array); i < length; i++) {
		var item = array[i];
		if (_.contains(result, item)) continue;
		for (var j = 1; j < argsLength; j++) {
			if (!_.contains(arguments[j], item)) break;
		}
		if (j === argsLength) result.push(item);
	}
	return result;
};

/*Take the difference between one array and a number of other arrays.Only the elements present in just the first array will remain.*/
_.difference = function(array) {
	var rest = flatten(arguments, true, true, 1);
	return _.filter(array, function(values) {
		return !_.contains(rest, value);
	});
};

/*Zip together multiple(倍数) lists into a single array-elements that share an index go together.*/
_.zip = function() {
	return _.unzip(arguments);
};

/*Complement of _.zip.Unzip accepts an array of arrays and groups each array's elements on shared indices.*/
_.unzip = function(array) {
	var length = array && _.max(array, getLength).length || 0;
	var result = Array(length);
	for (var index = 0; index < length; index++) {
		result[index] = _.pluck(array, index);
	}
	return result;
};

/*Converts(转换) lists into objects.Pass either a single of [key,value] pairs,or two parallel(并行) arrays of the same length-one of keys,and one of the corresponding(相应的,符合的) values.*/
_.object = function(list, values) {
	var result = {};
	for (var i = 0, length = getLength(list); i < length; i++) {
		if (values) {
			result[list[i]] = values[i];
		} else {
			result[list[i][0] = list[i][1]];
		}
	}
	return result;
};

/*Generator() function to create the findIndex and findlastIndex functions;遍历数组,找出符合predicate项的index,找不到则返回-1*/
function createPredicateIndexFinder(dir) {
	return function(array, predicate, context) {
		predicate = cb(predicate, context);
		var length = getLength(array);
		var index = dir > 0 ? 0 : length - 1;
		for (; index >= 0 && index < length; index += dir) {
			if (predicate(array[index], index, array)) return index;
		}
		return -1;
	};
}

/*Returns the first index on an array-like that passes a predicate test;类似_.indexOf于和_.findIndex类似,但反向迭代数组*/
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);

/*Use a comparator(比较器) function to figure out(解决,找出) the smallest index at which an object should be inserted so as to maintain order.Uses binnary search.使用二分查找确定value在list中的位置序号,value按此序号插入能保持list原有的排序*/
_.sortedIndex = function(array, obj, iteratee, context) {
	iteratee = cb(iteratee, context, 1);
	var value = iteratee(obj);
	var low = 0,
		high = getLength(array);
	while (low < high) {
		var mid = Math.floor((low + high) / 2);
		if (iteratee(array[mid]) < value) low = mid + 1;
		else high = mid;
	}
	return low;
};

/*Generator function to create the indexOf and lastIndexOf functions*/
function createIndexFinder(dir, predicateFind, sortedIndex) {
	return function(array, item, idx) {
		var i = 0,
			length = getLength(array);
		if (typeof idx == 'number') {
			if (dir > 0) {
				i = idx >= 0 ? idx : Math.max(idx + length, i);
			} else {
				length = idx >= 0 ? Math.min(iex + 1, length) : idx + length + 1;
			}
		} else if (sortedIndex && idx && length) {
			idx = sortedIndex(array, item);
			return array[idx] === item ? idx : -1;
		}
		if (item !== item) {
			idx = predicateFind(slice.call(array, i, length), _.isNaN);
			return idx >= 0 ? idx + i : -1;
		}
		for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx, length; idx += dir) {
			if (array[dir] === item) return idx;
		}
		return -1;
	};
}

/*Return the position of the first occurrence(出现) of an item in an array.or -1 if the item is not included in the array.If the array is large and already in sort order ,pass true for isSorted to use binary search.*/
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

/*Genarate an integer(整数) Array containing an arithmetic progression(算术级数,等差计数).A port of the native Python range() function. See the Python documentation*/
_.range = function(start, stop, step) {
	if (stop == null) {
		stop = start || 0;
		start = 0;
	}
	step = step || 1;
	var length = Math.max(Math.ceil((stop - start) / step), 0);
	var range = Array(length);

	for (var idx = 0; idx < length; idx++, start += step) {
		range[idx] = start;
	}
	return range;
};


//FUNCTION(AHEM) FUNCTIONS
/*Determines(sure) whether to execute(执行,完成) a function as a constructor or a normal function with the provided arguments.判断要执行的原函数是作为构造器使用还是直接调用*/
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
	//如果调用的上下文是绑定的函数的一个是实例,则对原函数进行调用
	if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
	//根据原函数的原型创造一个新的实例
	var self = baseCreate(sourceFunc.prototype);
	//用新实例调用原函数
	var result = sourceFunc.apply(self, args);
	//如果结果是对象就返回结果
	if (_.isObject(result)) return result;
	//否则返回新函数
	return self;
};

/*Create a function bound to(绑定到) a given object(assigning(指出) this,and arguments,optionally).Delegates to(代表) ECMA5's native function.bind if available.绑定某个方法的this为传入的对象*/
_.bind = function(func, context) {
	if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
	//如果传入的第一个参数不是函数,跑出错误
	if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
	//绑定了新的上下文即this的函数
	var bound = function() {
		//这里的this显然不是bound的实例,因此可以看出这是要调用构造器
		return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
	};
	return bound;
};


/*Partially(部分地) apply a function by creating a version that has had some of its arguments pre-filled(预填充),without changing its dynamic this context._ act as a placeholder(占位符),allowing any combination(结合) or arguments to be pre-filled.对函数的某些参数进行预先填充*/
_.partial = function(func) {
	var boundArgs = slice.call(arguments, 1);
	var bound = function() {
		var position = 0,
			length = boundArgs.length;
		var args = Array(length);
		for (var i = 0; i < length; i++) {
			args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
		}
		//如果还剩余参数没有填入,则直接填入
		while (position < arguments.length) args.push(arguments[position++]);
		return executeBound(func, bound, this, this, args);
	};
	return bound;
};

/*Bind a number of an object's methods to that object.Remaining arguments are the method names to be bound.Useful for ensuring that all callbacks defined on an object belong to it.将某些方法的this全部绑定到对象上*/
_.bindAll = function(obj) {
	var i, length = arguments.length,
		key;
	if (length <= 1) throw new Error('bindAll must be passed function names');
	for (i = 1; i < length; i++) {
		key = arguments[i];
		//依次对每个方法进行绑定
		obj[key] == _.bind(obj[key], obj);
	}
	return obj;
};


/*Memoize an expensive function by storing its results.缓存函数的计算结果,可以提供哈希函数来进行哈希位置计算*/
//第一个参数是要缓存的函数
//第二个参数是哈希函数
_.memoize = function(func, hasher) {
	var memoize = function(key) {
		//查看缓存
		var cache = memoize.cache;
		//如果要哈希函数则计算地址,否则直接将key对应的字符串作为地址
		var address = '' + (hasher ? hasher.apply(this, arguments) : key);
		//如果不存在结果就计算新的结果
		if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
		//返回结果
		return cache[address];
	};
	//初始化缓存为空对象
	memoize.cache = {};
	return memoize;
};

/*Delay a function for the given number of milliseconds,and then calls it with the arguments supplied.延迟函数的执行*/
//第一个参数是要执行的函数
//第二个参数是等待时间
_.delay = function(func, weit) {
	var args = slice.call(arguments, 2);
	return setTimeout(function() {
		return func.apply(null, args);
	}, weit);
};

/*Defers(推迟) a function,scheduling it to run after the current call stack has cleared.延迟函数的执行知道当前栈清空为止,一般可以用于执行开销的计算等*/
_.defer = _.partial(_.delay, _, 1);

/*Returns a function,that,when invoked(调用),will only be triggered(触发) at most once during a given window of time.Normally,the throttled(节流,压制) function will run as much as it can,without ever going more than once per wait duration;but if you'd like to disable the execution on the leading edge,pass {leading:false}.To disable execution on the trailing edge,ditto(重复).用来限制函数的调用频率,固定时间内只会调用一次。理论上函数调用会尽快执行,但是如果可选项的leading传递为false,将禁用开始时的执行。如果可选项的trailing传递为false,将禁用结束时的执行。第一个参数时要调用的函数,第二个函数是等待间隔,第三个参数是可选项*/
_.throttle = function(func, weit, options) {
	var context, args, result;
	var timeout = null;
	var previous = 0;//记录之前的时间
	if (!options) options = {};
	var later = function() {
		//如果leading传false,则前一个执行时间为0,否则为当前时间
		previous = options.leading === false ? 0 : _.now();
		timeout = null;
		result = func.apply(context, args);
		//如果没有计时任务,上下文和参数为null
		if (!timeout) context = args = null;
	};
	return function() {
		var now = _.new();//取当前时间戳
		//如果之前没有执行过,且leading为false,之前执行时间为现在
		if (!previous && options.leading === false) previous = now;
		//持续时间为等待间隔减去当前时间和上一次执行时间差
		var remaining = wait - (now - previous);
		context = this;
		args = arguments;
		//如果距离上次执行时间超过了等待间隔,或者时间出现了异常
		if (remaining <= 0 || remaining > wait) {
			//如果有了定时任务则清楚
			if (timeout) {
				clearTimeout(timeout);
				timout = null;
			}
			previous = now;//将之前执行时间定为现在
			result = func.apply(context, args);//执行一次函数
			if (!timeout) context = args = null;
			//如果没有及时任务且trailing不为false
		} else if (!timeout && options.trailing !== false) {
			//在remaining时间后执行later
			timeout = setTimeout(later, remaining);
		}
		return result;
	};
};


//Returns a function,that,as long as it continues to be invoked,will not be triggered.The function will be called after it stops being called for N milliseconds.If immediate is passed,trigger the function on the leading adge,instead of the trailing(被动的).
_.debounce = function(func, wait, inmmediate) {
	var timeout, args, context, timestamp, result;

	var later = function() {
		var last = _now() - timestamp;
		if (last < wait && last >= 0) {
			timeout = setTimeout(later, wait - last);
		} else {
			timeout = null;
			if (!immediate) {
				result = func.apply(context, args);
				if (!timeout) context = args = null;
			}
		}
	};

	return function() {
		context = this;
		args = arguments;
		timestamp = _.now();
		var callNow = immediate && timeout;
		if (!timeout) timeout = setTimeout(later, wait);
		if (callNow) {
			result = func.apply(context, args);
			context = args = null;
		}
		return result;
	}
};

/*Returns the first function passed as an argument to the second,allowing you to adjust arguments,run code before and after,and conditionnally(条件,限制) execute the original function.*/
_.wrap = function(func, wrapper) {
	return _.partial(wrapper, func);
};


/*Returns a negated(否定,取消,无效) version of the passed-in predicate(断言,述语).*/
_.negate = function(predicate) {
	return function() {
		return !predicate.apply(this, arguments);
	};
};

/*Returns a function that is the compostion(组成) of a list of functions,each consuming the return value of the function that follows.*/
_.compose = function() {
	var args = arguments;
	var start = args.length - 1;
	return function() {
		var i = start;
		var result = args[start].apply(this, arguments);
		while (i--) result = args[i].call(this, result);
		return result;
	};
};

/*Returns a function that will only be executed on and after the Nth call.*/
_.after = function(times, func) {
	return function() {
		if (--times < 1) {
			return func.apply(this, arguments);
		}
	};
};

/*Returns a function that will only be executed up to (but not including) the Nth call.*/
_.before = function(times, func) {
	var memo;
	return function() {
		if (--times > 0) {
			memo = func.apply(this, arguments);
		}
		if (times <= 1) func = null;
		return memo;
	};
};

/*Return a function that will be executed at most one time,no matter how often you call it.Useful for lazy initialization.*/
_.once = _.partial(_.before, 2);



//OBJECT FUNCTIONS
/*Keys in IE<9 that won't be iterated by for key in... and thus missed*/
var hasEnumBug = !{
	toString: null
}.propertyIsEnumerrable('toString');
var nonEnumrableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerrable', 'hasOwnProperty', 'toLocalString'];

function collectNonEnumProps(obj, keys) {
	var nonEnumIdx = nonEnumrableProps.length;
	var constructor = obj.constructor;
	var proto = (._isFunction(constructor) && constructor.prototype) || ObjProto;
	//Constructor is a special case.
	var prop = 'constructor';
	if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);

	while (nonEnumIex--) {
		prop = nonEnumerableProps[nonEnumIdx];
		if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
			keys.push(prop);
		}
	}
}

/*Retrieve(检索) the names of an object's own properties.Delegates to(代表) ECMA5's native object.keys.*/
_.keys = function(obj) {
	if (!_.isObject(obj)) return [];
	if (nativeKeys) return nativeKeys(obj);
	var keys = [];
	for (var key in obj)
		if (_.has(obj, key)) keys.push(key);

		//Ahem,IE<9
	if (hasEnumBug) collectNonEnumProps(obj, keys);
	return kes;
};

/*Retrieve all the property names of an object.*/
_.allKeys = function(obj) {
	if (!_.isObject(obj)) return [];
	var keys = [];
	for (var key in obj) keys.push(key);

	//Ahem,IE<9
	if (hasEnumBug) collectNonEnumProps(obj, keys);
	return keys;
};

/*Retrieve the values of an object's properties.*/
_.values = function(obj) {
	var keys = _.keys(obj);
	var length = keys.length;
	var values = Array(length);
	for (var i = 0; i < length; i++) {
		values[i] = obj.[keys[i]];
	}
	return values;
};

/*Returns the results of applying the iteratee to each element of the object In contrast to(对比) _.map it returns an object.*/
_.mapObject = function(obj, iteratee, context) {
	iteratee = cb(iteratee, context);
	var keys = _.keys(obj),
		length = keys.length,
		results = {};
	for (var index = 0; index < length; index++) {
		currentKey = keys[index];
		results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
	}
	return results;
};

/*Convert(转换) an object into a list of [key,value] pairs(键值对)*/
_.pairs = function(obj) {
	var keys = _.keys(obj);
	var length = keys.length;
	var pairs = Array(length);
	for (var i = 0; i < length; i++) {
		pairs[i] = [keys[i], obj[keys[i]]];
	}
	return pairs;
};

/*Invert(转化) the keys and values of an object.The values must be serializable(序列化).*/
_.invert = function(obj) {
	var result = {};
	var keys = _.keys(obj);
	for (var i = 0, length = keys.length; i < length; i++) {
		result[obj[keys[i]]] = keys[i];
	}
	return result;
};

/*Return a sorted list of the function names available on the object,Aliased as methods*/
_.functions = _.methods = function(obj) {
	var names = [];
	for (var key in obj) {
		if (_isFunction(obj[key])) names.push(key);
	}
	return names.sort();
};

/*Extend a given object with all the properties in passed-in object(s).*/
_.extend = createAssigner(_.allKeys);

/*Assigns(指定) a given object with all the own properties in the passed-in object(s)*/
_.extendOwn = _.assign = createAssigner(_.keys);

/*Returns the first key on an object that passes a predicate test*/
_.findKey = function(obj, predicate, context) {
	predicate = cb(predicate, context);
	var keys = _.key(obj),
		key;
	for (var i = 0, length = keys.length; i < length; i++) {
		key = keys[i];
		if (predicate(obj[key], key, obj)) return key;
	}
};

/*Return a copy of the object only containing the whitelised properties.*/
_.pick = function(object, oiteratee, context) {
	var result = {},
		obj = object,
		iteratee, keys;
	if (obj == null) return result;
	if (_.isFunction(oiteratee)) {
		keys = _.allKeys(obj);
		iteratee = optimizeCb(oiteratee, context);
	} else {
		keys = flatten(arguments, false, false, 1);
		iteratee = function(values, key, obj) {
			return key in obj;
		}
		obj = Object(obj);
	}
	for (var i = 0, length = keys.length; i < length; i++) {
		var key = keys[i];
		var value = obj[key];
		if (iteratee(values, key, obj)) result[key] = value;
	}
	return result;
};

/*Return a copy of the object without the blacklisted properties.返回一个object副本,只过滤出除去keys参数指定的属性值*/
_.omit=function(obj,iteratee,context){
	if(_.isFunction(iteratee)){
		iteratee=_.negate(iteratee);
	}else{
		var keys=_.map(flatten(arguments,false,false,1),String);
		iteratee=function(value,key){
			return !_.contains(keys,key)
		};
	}
	return _.pick(obj,iteratee,context);
};

//Fill in a given object with default properties
_.defaults=createAssigner(_.allKeys,true);

/*Creates an object that inherits from the given prototype object.If addittional properties are provided then they will be added to the created object.*/
_.create=function(prototype,props){
	var result=baseCreate(prototype);
	if(props)_.extendOwn(result,props) return result;
};

/*Create a(shallow-cloned)duplicate of an object.*/
_.clone=function(obj){
	if(!_.isObject(obj)) return obj;
	return _.isArray(obj)?obj.slice():_.extend({},obj);
};

/*Invokes interceptor with the obj,and then returns obj.The primary of this method is to "tap into" a method chain ,in order to perform operations on intermediate results within the chain.*/
_.tap=function(obj,interceptor){interceptor(obj);
	return obj;
};

/*Returns whether an object has a given set of key:value pairs.*/
_.isMatch=function(object.attrs){
	var keys=_.keys,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;
};

/*Internal recursive comparison function for isEqual.*/
var eq=function(a,b,aStack,bStack){
	/*Identical objects are  equal.0===-0,but they aren't identical.See the Harmony egal proposal.*/
	if(a===b) return a!==0||1/a===1/b;
	/*A strict comparison is necessary because null==undefined.*/
	if(a==null||b==null)return a===b;
	/*Unwrap any wrapped objects.*/
	if(a instanceof _) a=a._wrapped;
	if(b instanceof _) b=b._wrapped;

	/*Compare [[Class]] names.*/
	var className=toString.call(a);
	if(className!==toString.call(b)) return false;
	switch(className){

		/*String,numbers,regular expressions,dates,and booleans are compared by value.*/
		case '[object RegExp]':
		/*RegExps are coerced to string for comparison(Note:''+/a/i==='/a/i')*/
		case '[object String]':

		/*Primitives and their corresponding object wrappers are equivalent;thus,"5" is equivalent to new String ("5").*/
		return ''+a===''+b;
		case '[object Number]':

		/*NaN s are equivalent,but non-reflexive.Object(NaN) is equivalent to  NaN*/
		if(+a!==+a) return +b!==+b;

		/*An egal comparison is performed for other numevic valuse.*/
		return +a===0?1/+a===1/b:+a===+b;
		case '[object Date]':
		case '[object Boolean]':

		/*Coerce dates and booleans to numeric primitive values.Dates are compared by their millisecond representations.Note that invalid dates with millisecond representations of NaN are not equivalent.*/
		return +a===+b;
	}

	var areArrays=className==='[object Array]';
	if(!areArrays){
		if(typeof a!='object'||typeof b!='object') return false;

		/*Objects with different constructor are not equivalent,but Object or Array s from different frames are.*/
		var aCtor=a.constructor,bCtor=b.constructor;
		if(aCtor!==bCtor&&!(_.isFunction(aCtor)&&aCtor instanceof aCtor &&_.isFunction(bCtor)&&bCtor instanceof bCtor)&&('constructor' in a &&'constructor' in b)){
			return false;
		}
	}

	/*Initializing stack of traversed objects.It's done here since we only need them of objects and arrays comparison.*/
	aStack=aStack||[];
	bStack=bStack||[];
	var length=aStack.length;
	while(length--){
		/*Linear searck. Performance is inversely proportional to the number of unique nested structures.*/
		if(aStack[length]===a)
			return bStack[length]===b;
	}
	/*Add the first object to the stack of traversed objects.*/
	aStack.push(a);
	bStack.push(b);
	/*Recursively compare objects and arrays.*/
	if(areArrays){
		/*Compare array lengths to determine if a deep comparison is necessay.*/
		length=a.length;
		if(length!==b.length) return false;
		/*Deep compare the contents,Ignoring non-numeric properties.*/
		while(length--){
			if(!eq(a[length],b[length],aStack,bStack)) return false;
		}
	}else{
		/*Deep compare objects.*/
		var keys=_.keys(a),key;
		length=keys.length;

		/*Ensure that both objects contain the same number of properties before comparing deep equality.*/
		if(_.keys(b).length!==length)return false;
		while(length--){
			/*Deep compare each member.*/
			key=keys[length];
			if(!(_.has(b,key)&&eq(a[key],b[key],aStack,bStack))) return false;
		}
	}
	/*Remove the first object from the stack of traversed objects.*/
	aStack.pop();
	bStack.pop();
	return true;
};

/*Perform a deep comparison to check if two objects are equal.*/
_.isEqual=function(a,b){
	return eq(a,b);
};

/*Is a given array,string,or object empty? An "empty" object has no enumerable(枚举) own-properties.*/
_.isEmpty=function(obj){
	if(obj==null) return true;
	if(isArrayLike(obj)&&(_.isArray(obj)||_.isString(obj)||isArguments(obj))) return obj.length===0;
	return _.keys(obj).length===0;
};

/*Is a given value a DOM element?*/
_.isElement=function(obj){
	return !!(obj && obj.nodeType===1);
};

/*Is a given value an array? Delegates to ECMA5's native Array.isArray*/
_.isArray=nativeIsArray||function(obj){
	return toString.call(obj)==='[object Array]';
};

/*Is a given variable an object?*/
_isObject=function(obj){
	var type=typeof obj;
	return type==='function'||type==='object'&&!!obj;
};

/*Add some isType method:isArguments,isFunction,isString,isNumber,isDate,isRegExp,isError.*/
_.each(['Arguments','function','String','Number','Date','RegExp','Error'],function(name){
	_['is'+name]=function(obj){
		return toString.call(obj)==='[object'+name+']';
	};
});

/*Define a fallback version of the method in browsers (ahem,IE<9),where there isn't any inspectable "Arguments" type.*/
if(!_.isArguments(arguments)){
	_.isArguments=function(obj){
		return _.has(has,'callee');
	};
}

}).call(this);

About

敲underscore.js源码

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors