1

In this code

if (direction === 'up') {
    for (key in elements) {
        if (elements.hasOwnProperty(key)) {
            elements[key].style.opacity = statics.elapsed / max_time;
        }
    }
} else if (direction === 'down') {
    for (key in elements) {
        if (elements.hasOwnProperty(key)) {
            elements[key].style.opacity = (max_time - statics.elapsed) / max_time;
        }
    }
}

I'd like to abstract out this common idiom:

for (key in elements) {
    if (elements.hasOwnProperty(key)) {
        // function using elements[key] and outside parameters
    }
}

so I could simply write

manyElements(element, function () {
});

How would I write the function prototype as starting point. My guess would be.

function manyElements (elements, the_function) {
    for (key in elements) {
        if (elements.hasOwnProperty(key)) {
            the_function();
        }
    }
}

but I need access to outside parameters, what is the best way to pass these in? Is this a good idea in general, to abstract out this code?

It seems like I would have to pass each parameter in individually defeating the purpose of making a general purpose function.

I don't prefer not to use an outside library, except for correlation. Per answer here is lodash implementation:

Snippet 1

  function forEach(collection, callback, thisArg) {
    if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
      var index = -1,
          length = collection.length;

      while (++index < length) {
        if (callback(collection[index], index, collection) === false) {
          break;
        }
      }
    } else {
      each(collection, callback, thisArg);
    }
    return collection;
  }

Snippet 2

var each = createIterator(eachIteratorOptions); 

Snippet 3

  function createIterator() {
    var data = {
      'arrayLoop': '',
      'bottom': '',
      'hasDontEnumBug': hasDontEnumBug,
      'isKeysFast': isKeysFast,
      'objectLoop': '',
      'nonEnumArgs': nonEnumArgs,
      'noCharByIndex': noCharByIndex,
      'shadowed': shadowed,
      'top': '',
      'useHas': true
    };

    // merge options into a template data object
    for (var object, index = 0; object = arguments[index]; index++) {
      for (var key in object) {
        data[key] = object[key];
      }
    }
    var args = data.args;
    data.firstArg = /^[^,]+/.exec(args)[0];

    // create the function factory
    var factory = Function(
        'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' +
        'nativeKeys, propertyIsEnumerable',
      'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
    );
    // return the compiled function
    return factory(
      createCallback, hasOwnProperty, isArguments, isString, objectTypes,
      nativeKeys, propertyIsEnumerable
    );
  }

2 Answers2

2

At first, some there are some libraries, like lodash or underscore that already do it. Check the http://lodash.com/docs#forEach function.

Secondly, you can use Object.keys(elements).forEach(function (key) { ... }) to shortcut the code.

And at third, you may need to read about the JavaScript functions - they can be passed to other functions as parameters and then called. Like this:

function do(something) {
 ...
 something(item);
 ...
}
do(function (arg) { console.log(arg); });
Alex Netkachov
  • 13,172
  • 6
  • 53
  • 85
  • interesting that Object.keys does not include the prototype chain - what I was looking for, ie9+ unless using work around. –  Feb 04 '13 at 02:12
1

If you do this several times in your code, there is nothing wrong with abstracting it out. You almost have the answer already too: Just pass the parameters you need back into the function that you passed into manyElements. I'd go for passing both the key and the value, since you might need the key in some places (e.g. to change the assignment to something else).

function manyElements (elements, the_function) {
    for (key in elements) {
        if (elements.hasOwnProperty(key)) {
            the_function(key, elements[key]);
        }
    }
}

Usage would be like this:

if (direction === 'up') {
    manyElements(elements, function (key, value) {
        value.style.opacity = statics.elapsed / max_time;
    });
} else if (direction === 'down') {
    manyElements(elements, function (key, value) {
        value.style.opacity = (max_time - statics.elapsed) / max_time;
    });
}

I wouldn't call the function manyElements though, because that name doesn't really explain what the function does. Something like forEachProperty might be more self-documenting.

Medo42
  • 3,821
  • 1
  • 21
  • 37