-2

While I was at home playing some Javascriptz and, I faced something...

I made a simple function that returns each char from a string or each position value from an array:

function each (haystack, callback) {
    var each;
    for (each in haystack){
        if (callback && typeof callback == "function") {
            callback (haystack[each]);
        }
    }
}

How to use:

each ("StackOverflow", function (c) {
    console.log(c);
});

I was wondering if there was a way I could turn /\ that, into this:

each ("StackOverflow", c) {
    console.log(c);
}

Or at least something like this, anything that you don't need to rewrite the word function for the callback.

I tried this, but with no success:

each ("StackOverflow",
    "console.log(c)" // turn the callback function into a string
    // but where was `c` defined here? do.not.ask.me.
);

And the each() was:

function each (haystack, callback) {
    var each;
    for (each in haystack){
        // call it in a setTimeout() crazy? yeah, didn't work...
        setTimeout(callback, 0);

        //and also tried this, no success...
        var b = new Function ("haystack[each]", cb);
        b ();
    }
}

My conclusion here is, unfortunately we can't have a callback function without declaring an anonymous function.

Is there a way I can make a callback function without using the work function?

Jon Adams
  • 24,464
  • 18
  • 82
  • 120
BernaMariano
  • 846
  • 2
  • 9
  • 27

5 Answers5

4

Please forget the string representation version, it always involves eval(), which we know is evil.

You can't simply change the syntax of a programming language.

Writing function () {} is not really a big nuisance anyways, you can even configure your editor to insert the snippet automatically. You can also use CoffeeScript, which has an easier syntax for functions (besides several other features of course) and compiles to Javascript.

Also, you don't HAVE TO use an anonymous function. You can pass any kind of function.

var func = function (x) { console.log(x); }; //inline function
each ("StackOverflow", func);

function func2 (x) { console.log(x); }; //classic function
each ("StackOverflow", func2);

each ("StackOverflow", alert); //YEEEES

each ("StackOverflow", 
    console.log.bind(console)); //here you should bind it to console!
kapa
  • 77,694
  • 21
  • 158
  • 175
  • 1
    Strictly, an "anonymous function" is a function expression with no name. Function expressions aren't declared, they are only evaluated. – RobG Dec 21 '12 at 01:41
  • Your last example won't work obviously, because `console.log` needs the proper context; see also my answer. – Ja͢ck Dec 21 '12 at 01:43
  • @Jack Obviously yes. I better go to sleep. Upvoted your answer :). – kapa Dec 21 '12 at 01:45
  • @BernaMariano Binds the function to a certain context (`this` will always point to `console` inside the function). [MDN Link](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind). – kapa Dec 21 '12 at 07:14
  • the 3rd one? `each ("StackOverflow", alert);` – BernaMariano Dec 21 '12 at 09:20
  • @BernaMariano `alert` is a function like all the others (`window.alert` actually), and it expects one parameter, so it can be simply used here. – kapa Dec 21 '12 at 11:34
  • it doesn't need to be a `function(){ alert() }` – BernaMariano Dec 21 '12 at 12:17
  • 1
    @BernaMariano Try it ;). You have to pass a function. Any function. – kapa Dec 21 '12 at 12:27
3
for (each in haystack)

If you expect haystack to be an array, you should iterate it with a for-loop instead of enumerating keys with a for-loop (see JavaScript for...in vs for for the difference). This is especially true if you iterate strings, and please notice that older IEs do not support bracket notation on strings.

we can't have a callback function without declaring an anonymous function

No. For a callback function, of course we need some function. But it does not necessarily need to be an anonymous function expression. In your example, you could just do

each ("StackOverflow", console.log);
each ("StackOverflow", console.log.bind(console)); // for Chrome users

…or pass any other suitable function that you have declared elsewhere.

Strings (like in this library) might be even shorter sometimes, but they would need to be evaled which is evil for this purpose. Unfortunately EcmaScript does not support any shorter lambda expressions, however you might want to have a look at FF-only JavaScript 1.8 expression closures or CoffeeScript's minimalist function syntax.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
3

There are exactly two four ways to create a function in ECMAScript

  1. A FunctionDeclaration
  2. A FunctionExpression
  3. The Function constructor called as a function
  4. The Function constructor called as a constructor

All require the use of the string "function" or "Function".

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Theoretically you could use the `Function()` constructor also, but we are at using `eval()` again. – kapa Dec 21 '12 at 01:41
2

Short answer: no.

You could adopt a compile-to-javascript language like Coffeescript that has a shorter syntax for anonymous functions:

//javascript version
function(c){ console.log(c) }

//coffeescript version
(c) => console.log c

Otherwise, you're going to have to declare the function somewhere, be it 'in-place' (anonymous functions) or just somewhere else in your code as a variable.

Dylan
  • 13,645
  • 3
  • 40
  • 67
2

To use console.log as a callback directly, you have to make sure to bind it back to console lest it loses the correct execution context, at least in Chrome so I'm told :)

each([1, 2, 3], console.log.bind(console));

Also, your each() doesn't work safely with that for ... in:

for (var i = 0, n = haystack.length; i != n; ++i) {
    callback(haystack[i]);
}

The difference is more obvious when extra properties have been added to an Array object.

See also: Why is using "for...in" with array iteration a bad idea?

Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309