说明
1、源码结构通览,简单注释说明
2、通过调用方法讲解核心代码逻辑
一、源码的结构
为了方便比对源码,按源码的结构顺序展示。
underscore是个轻量级的工具库,大部分代码是实现特定功能以函数的形式存在,本身会比较简单,没对方法具体说明,可直接参考underscore中文文档
(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));
二、两种调用方法
1、最常用的调用方式_.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函数。此方法比较简单易懂
2、OOP风格,类jquery调用_(arguments1).method(arguments2)
示例2:
var arr = [1, 2, 3];
// OOP风格,类jquery调用
var test2=_(arr).map(function(num) {
return num* 3;
});
示例3:链式调用
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);
调用流程主要代码:
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;
};