我在Underscore的_.each
功能中遇到了一些令人困惑的行为。我在基类中有以下内容,用于创建一组对象:
constructor(properties = {}, options = {}) {
// lets me replace UUID generator function for testing, and for a
// special-case id used by a singleton subclass
_.defaults(options, {generateUuid});
_.chain(properties)
.keys()
.reject((k) => k === 'id' || k === '_id')
.each((k) => this[k] = properties[k])
.value();
this._id = options.generateUuid();
}
在测试我的第一个大型子类的构造函数时,我传入了大量属性(数字,字符串和布尔值)。我的Chai断言在第43个属性(61)失败,紧接着第一个false
值。该错误声称该财产不存在。
有问题的子类的当前状态是:
constructor(properties = {}) {
if (typeof properties.subtype !== 'undefined') {
properties._subtype = properties.subtype;
}
// etc. for several more aliased properties
_.defaults(properties, {
_subtype: 'token',
// etc. for default property values
});
super({
_type: 'graphic',
_subtype: properties._subtype,
// etc. for all whitelisted properties
});
}
在尝试调试问题时,我确认所有属性及其正确值都被传递给超类构造函数,但第一个false
之后的所有属性都没有添加到{{1} }。只有当我向this
添加跟踪调试时才真正改变了事情:
.each
突然,所有属性都出现在对象中,我的测试通过了!由于单行箭头函数(没有括号)返回其单个表达式的值(而多行箭头函数默认返回undefined),而表达式_.chain(properties)
.keys()
.reject((k) => k === 'id' || k === '_id')
.each((k) => {
console.log(k);
this[k] = properties[k];
})
.value();
返回bar,我唯一的结论是从foo = bar
返回false
结束了循环。
所以,我开始调查为什么会这样。我抓住了Underscore源代码(v1.8.3,运行相同的版本)来查看是否有专门用于完成该结果的特殊处理(这将方便地允许_.each
的客户端代码来自{{ 1}}循环):
break
如果_.each
是假的,_.each = _.forEach = function(obj, iteratee, context) {
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;
};
会立即返回context
。当optimizeCb(iteratee, context)
返回false时,这里没有什么可以提前爆发。
有没有人对我观察到的行为有解释?
答案 0 :(得分:3)
您好像使用的是lodash而不是underscore。
使用三个参数调用iteratee:(value,index | key,collection)。 Iteratee函数可以通过显式返回
false
来提前退出迭代。