Underscore.js源码学习笔记

Posted by WWJ on August 27, 2018

underscore源码学习

将内置对象上的一些方法事先用一个变量给保存起来,以免每次需要使用Array.prototype、Object.prototype、Function.prototype的时候都要重新去获取,节省了时间,我觉得这是一个比较好的习惯,学习了。

var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

var
    push             = ArrayProto.push,
    slice            = ArrayProto.slice,
    toString         = ObjProto.toString,
    hasOwnProperty   = ObjProto.hasOwnProperty;

支持无 new调用构造函数

就是支持在构造函数内部返回构造函数的实例

// 面对对象编程的时候才会进入函数内部
// 像_([1,2,3,4]).each(alert)
// _([1,2,3,4]) 相当于无 new构造了一个对象
// 如果直接使用_.each(alert)的话,就不会执行这个函数了
var _ = function(obj) {
	if (obj instanceOf _) return obj;
	if (!(this instanceOf _)) return new _(obj);
}

optimizeCb (func, context, argCount)和 cb(func, context, argCount)的运用场景

optimizeCb函数适用于func的类型只是Function,像each、forEach的回调函数,而cb函数则适用于func的类型不只是Function,像map函数,可能还是对象,或者是基本类型的值,当func的类型是Function的时候,那就跟optimizeCb一样,返回func;当func的类型是对象的时候,返回map的每一项是否符合func的布尔值;当func的类型是基本类型的值的时候,就返回map的每一项的值

// optimizeCb (func, context, argCount)
// argCount这个参数的作用就是为了调用call函数,如果不用这个参数,直接调用apply也是一样的,但就是因为call比apply快很多
optimizeCb = function(func, context, argCount) {
	// 使用call

	// 如果argCount没有传入的话,就默认为3
	let argCount === argCount === void 0 ? 3 : argCount;
	switch (argCount) {
		case 1: return function(value) {
			return func.call(context, value);
		}
		case 2: return function(value, others) {
			return func.call(context, value, others);
		}
		case 3: return function(value, index, collection) {
			return func.call(context, value, index, collection);
		}
		
		// 适用于reduce函数
		case 4: return function(accumulator, value, index, collection) {
			return func.call(context, accumulator, value, index, collection);
		}
	}

	// 使用apply,因为不需要考虑argCount的个数,那么就不需要switch-case条件语句了,直接
	return function () {
		return func.apply(context, arguments);
	}
}


// cb (func, context, argCount)
cb = function(func, context, argCount) {
	// 当func的类型是Function的时候,那就跟optimizeCb一样,返回func;这里可以直接调用optimizeCb函数了
	if (_.isFunction(func)) return optimizeCb(func, context, argCount);
	// 当func的类型是对象的时候,返回map的每一项是否符合func的布尔值;
	if (_.isObject(func)) return _.matcher(func);
	// 当func的类型是基本类型的值的时候,就返回map的每一项的值;
	return _.property(func);
}

合并对象(createAssigner)

这个函数主要的作用是提供一个闭包函数

createAssigner = function(keyFunc, undefinedOnly) {
	return function(obj) {
		let length = arguments.length;
		if (length < 2 || !obj) return obj
		for (let i = 1; i < length; i++) {
			let source = arguments[i];
				keys = keyFunc(source);
				len = keys.length;
			for (let j = 0; j < len; j++) {
				let key = keys[j];
				if (!undefinedOnly || obj[key] === void 0) {
					obj[key] = source[key];
				}
			}
		}
		return obj;
	}
}

// 会将obj的所有属性(包括继承的)一起复制,_.allKeys函数会返货对象的所有key,(包括原型上的),后面的覆盖前面的属性值
_.extend = createAssigner(_.allKeys, obj)
// 会将obj的所有属性(不包括包括继承的)一起复制,_.allKeys函数会返货对象的所有key,(不包括原型上的),后面的会覆盖前面的属性值
_.extendOwn = createAssigner(_.keys, obj);
// 会将obj的所有属性(包括继承的)一起复制,_.allKeys函数会返货对象的所有key,(包括原型上的),不会覆盖
_.defaults = createAssogner(_allkeys, true, obj);