tl;dr
もったいぶって書いてますが、CoffeeScript
の関数の最後の値がなんであってもreturn
されます。
_.forEach
であればコールバックでreturn false
されると巡回が終わってしまう。これに最後の行にbool = false
的なものを書くとreturn bool = false
となり、意図せずfalse
がreturn
されてしまうので、気を付けましょう。
lodashの_.forEachがすべての要素を巡回しない問題
例えば全部のactive
プロパティをfalse
にしようとします。
describe '_.forEach', ->
objects = null
beforeEach ->
objects = [
id: '1'
active: true
,
id: '2'
active: true
,
id: '3'
active: true
]
case1
it 'should make all inactive', ->
_.forEach objects, (obj)->
obj.active = false
expect object[0].active
.toBe false
expect object[1].active
.toBe false
expect object[2].active
.toBe false
あれ?失敗するぞ。
case2
it 'should sweep all element', ->
count = 0
_.forEach objects, (obj)->
count = count + 1
obj.active = false
expect count
.toBe 3
あれれ?また失敗するぞ。
case2を修正
it 'should sweep all element', ->
count = 0
_.forEach objects, (obj)->
obj.active = false
count = count + 1
expect count
.toBe 3
おっ、成功した。
count = count + 1
とobj.active = false
を入れ替えると結果が変わる。
ああ、いつでも忘れてはいけないこの仕様……
JavaScriptを見てみる
it('should sweep all element', function() {
var count;
count = 0;
_.forEach(objects, function(obj) {
count = count + 1;
return obj.active = false;
});
return expect(count).toBe(3);
});
it('should sweep all element', function() {
var count;
count = 0;
_.forEach(objects, function(obj) {
obj.active = false;
return count = count + 1;
});
return expect(count).toBe(3);
});
そう、犯人はreturn obj.active = false;
です。
lodashのドキュメントに書いてあります
Iterates over elements of collection invoking iteratee for each element. The iteratee is bound to thisArg and invoked with three arguments; (value, index|key, collection). Iterator functions may exit iteration early by explicitly returning false.
https://lodash.com/docs#forEach
_.forEach
のコールバックでreturn false
すると巡回を止めてしまいます。
it 'should make all inactive', ->
_.forEach objects, (obj)->
obj.active = false
true
expect objects[0].active
.toBe false
expect objects[1].active
.toBe false
expect objects[2].active
.toBe false
これでOK。
ちなみに
jQuery
の$.each
も同じ仕様のようですが、AngularJS
のangular.forEach
は巡回をやめないようです(!!)