-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
概览
optimizeCb、cb 和 restArguments 三个方法是 Underscore.js 中重要的三个内部函数。
optimizeCb
由一道题引发的思考。
const subjects = ["math", "biology", "English"];
const result = subjects.map(
function(subject, index) {
return this[index];
},
[1, 2, 3]
);
console.log(result); // [1, 2, 3]查看 MDN 中 map 的用法可以得知, map(callback(currentValue[, index[, array]]) [, thisArg])
thisArg 是执行 callback 函数时 this 的指向。在 Underscore.js 里,库的创作者也要考虑
这种情况。
// underscore.js v1.9.2
var optimizeCb = function(func, context, argCount) {
/*
绝大多数情况下,不传 context, 直接返回原原本本传入的函数。
不会走下面的 switch 流控制语句
*/
if (context === void 0) return func;
/*
当 context 不为 undefined 时
*/
switch (argCount == null ? 3 : argCount) {
/*
用在 _.times 方法中
_.times = function(n, iteratee, context) {
var accum = Array(Math.max(0, n));
iteratee = optimizeCb(iteratee, context, 1);
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum;
};
只有传入的第一个参数有用
*/
case 1:
return function(value) {
return func.call(context, value);
};
// The 2-argument case is omitted because we’re not using it.
/*
一般都是这种情况,像 map、filter等等
*/
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);
};
}
/*
只单纯改变执行上下文
*/
return function() {
return func.apply(context, arguments);
};
};分析 optimizeCb 方法的源码可知,该方法只处理 func 参数为函数类型的情况。
Underscore.js 里对 context 分了五种情况处理
- 没有传入 context 时,直接返回原原本本传入的函数;
- argCount 为 1 时,改变方法的上下文,只接受传入的第一个参数,只为 _.times 方法服务
- 不传 argCount,改变方法的上下文,接受三个参数,用于 map、filter等方法
- argCount 为 4 时,改变方法的上下文,接受三个参数,用于 reduce 等方法
- 其他情况,单纯改变方法的上下文
cb
cb 函数与 optimizeCb 都起优化传入的方法的作用。
optimizeCb 函数主要优化传入的方法,并绑定方法的执行上下文;
cb 函数负责处理传入的值所有情况。
// underscore.js v1.9.2
_.map = _.collect = function(obj, iteratee, context) {
/*
map 方法首次用到了 cb 这个内部函数
*/
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
/*
此处的 for 循环是 _.map 方法 核心
为 Array、ArrayLike, 那么 result = array[index]
为 Object, 那么 result = object[key]
*/
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};仅仅看这行代码,我们不妨猜测 cb 函数的作用:
iteratee = cb(iteratee, context);cb 函数对传入的 iteratee 进行处理,最后将返回的结果赋值给 iteratee 变量。
在 JavaScript 中, 一个变量的值可能是下面八种数据类型。
Number String Boolean undefined null BigInt Symbol Object我们来看看 cb 函数的源码。
// underscore.js v1.9.2
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
/*
_.identity = value => value
若 value 为 null、undefined, 则返回一个函数
*/
if (value == null) return _.identity;
/*
通常情况下,如果 value 是函数类型,则丢给 optimizeCb 方法专门处理
*/
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
/*
若 value 是 Object 类型而不是 Array 类型
*/
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
/*
如果传入的是字符串或数组
返回对象的属性值
const books = [{name: 'qin'}, {name: 'xiao'}];
console.log(_.map(books, "name")); ['qin', 'xiao']
*/
return _.property(value);
};cb 方法用来优化传入的迭代、断言等函数。对于有的方法,比如 _.each 只能传递一个函数,
而 _.map、_.filter 等方法除了传递函数外,还可以传递字符串或数组参数。
restArguments
// underscore.js v1.9.2
var restArguments = function(func, startIndex) {
/*
func.length 即为函数参数的个数
*/
startIndex = startIndex == null ? func.length - 1 : +startIndex;
/*
返回一个函数
*/
return function() {
var length = Math.max(arguments.length - startIndex, 0),
/*
剩余参数组成一个数组
*/
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
case 0:
return func.call(this, rest);
case 1:
return func.call(this, arguments[0], rest);
case 2:
return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
Metadata
Metadata
Assignees
Labels
No labels