简体   繁体   中英

Why isn't 'return' breaking my loop?

Here is the test I am trying to pass:

describe("occur", function() {
  var getVal = function(i) { return i; };
  var even = function(num) { return num % 2 === 0; };

  it("should handle an empty set", function() {
    expect(occur([], getVal) ).toEqual(true);
  });

  it("should handle a set that contains only true values", function() {
    expect(occur([true, true, false], getVal)).toEqual(false);
  });

  it("should handle a set that contains one false value", function() {
    expect(occur([true, true, true], getVal)).toEqual(true);
  });

  it("should handle a set that contains even numbers", function() {
    expect(occur([0, 8, 32], even)).toEqual(true);
  });

  it("should handle a set that contains an odd number", function() {
    expect(occur([0, 13, 68], even)).toEqual(false);
  });
});

Here is my code:

var forEach = function(array, action){
  for (var i = 0; i < array.length; i ++){
    action(array[i]);
  }
};

var occur = function(array, blah){
  forEach(array, function(el){
    if(!blah(el)){
      return false;
    }
  });
    return true;  
};

What I believe I am doing in my occur function:

  1. Taking parameters(an array and a function
  2. Iterating over the array (in the forEach)
  3. If blah(el) isn't true, return false (shouldn't this break the loop and return false whenever there the function passed in evaluates to false?
  4. return true if there aren't any false values
  5. **I do not have a case currently implemented for an empty array.

Am I missing a trick with how return works? I provided a repl.it session of it below (link). I included a console.log inside my if statement and it does log 'false' when a value is false but the return value still doesn't output or break the loop.

http://repl.it/NjH/1

forEach does not return a value in Javascript-- when you return false , you're simply returning false from forEach 's callback. The value is ignored.

Instead, you could try Array.every() if you're using a compatible implementation like Node/Webkit/something that supports ECMAScript 5.

Otherwise, set a semaphore variable outside the forEach , and set it to false if blah(el) returns false. Then check the value of the variable after the forEach is complete.

You aren't missing a trick about return , you're missing one about .forEach .

For each basically looks like this:

function forEach (action) {
    var arr = this,
        i = 0, l = arr.length;

    for (; i < l; i += 1) {
        action(arr[i], i, arr);
    }
}

There's more to it than just that, but really, that's what it's doing.

So if action() has a return statement in it (and even if it doesn't, it just returns undefined , it doesn't matter to the loop at all.

It says "forEach" and what it will get you is one pass through every single item in the array (that exists at the time the function is called).

The core issue is that return always exits the nearest enclosing function - and no other.

var occur = function(array, blah){ // <-- "outer" function
  forEach(array, function(el){ // <-- callback to forEach / "enclosing" function
    if(!blah(el)){
      // This returns from the callback/enclosing function; it has no
      // bearing on the outer function.
      return false;
    }
  });
  return true;  
};

Barring changing approaches, this problem can be solved with using a variable.

var occur = function(array, blah){
  var ret = true;
  forEach(array, function(el){
    if(!blah(el)){
      // Set the closed-over variable to false, so that when the return
      // in the outer function is reached the correct value will be returned.
      // (forEach will still iterate over every remaining item)
      ret = false;
    }
  });
  return ret;  
};

Now, my recommendation is to use something like Array.some (which is in ES5.1 and supported in IE9+ and every modern browser of note).

var occur = function(array, blah){
  var any = array.some(function(el){
    if(!blah(el)){
      return true;
    }
  });
  return !any;
};

But really, changing the conditions (negations inside/outside and some to every ):

var occur = function(array, blah){
  return array.every(function(el){
    return blah(el);
  });
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM