3

Given:

function shout(){ alert('Hello!'); }
var dasarray = [ shout ];

shout();
dasarray[0]();

shout = function(){ alert('World!'); }

shout();
dasarray[0]();

Output:

Hello!

Hello!

World!

Hello!

I would've expected that dasarray received a reference to the function shout, but sadly the fourth output is also hello. If I assume right, I was able to modify the shout function correctly, but this leads to my assumption that the function was not passed by reference into dasarray.

Instead of an array I already tried this example with an object:

dasarray = { myfunction: shout }

With the same, disappointing, result.

My intention is to have an array/object of/with functions that are run in order and that can be extended in real time.

For example, extending the JavaScript code with new functionality and adding additional function calls to existing events that run in the correct order.

So, how can I put functions by reference into arrays/objects?


Thanks for the good answers. I just wanted to explain that I chose Eli's answer, because it lead me to this setup:

zzz = {
    calls: [],
    shout: function(message) {
        alert('Shouting: ' + message);
    },
    initialize: function () {
        zzz.calls.push(['shout','Hello World']);
        zzz.calls.push(['shout','this is']);
        zzz.calls.push(['shout','my last shout!']);
        zzz.run();

        zzz.shout = function(){ alert('modified!'); };
        zzz.run();
    },
    run: function () {
        $.each(zzz.calls, function(){ zzz[this[0]](this[1]); }); // <- Using jQuery $.each here, just to keep it easy
    }
};

zzz.initialize();
1
  • It is a reference to the function. You're changing what shout is pointing at, the array still references the same function. Commented Mar 15, 2013 at 17:31

2 Answers 2

7

I would've expected that dasarray received a reference to the function shout

It does. It does not receive a reference to the variable shout. (The symbol defined by the name of a function in a function declaration is, for virtually all intents and purposes, a variable — which is why you can assign to it later.) So your later line changing that variable:

shout = function(){ alert('World!'); }

...has no effect on the reference to the earlier function in dasarray.

Think of object references as a value saying where to find an object. When you do:

function shout(){ alert('Hello!'); }

...the symbol shout receives a value saying where that function is found, like this:

Variable shout pointing to function object

Then when you do:

var dasarray = [ shout ];

...that value is put in the first entry in the array. Now the symbol shout and the first entry in dasarray both have the same value, which tells the JavaScript engine where to find that function object in memory. So we have this:

Variable shout and dasarray entry 0 pointing to same function object

But the variable shout and the entry in dasarray have no connection whatsoever to each other.

Then you do this:

shout = function(){ alert('World!'); }

...that creates a new function and stores a new value in the variable shout saying where that new function is. But that has no effect on the value stored in dasarray's entry 0. We end up with this:

Var shout pointing to second function, dasarray entry 0 pointing to first function

I'm saying "value" a lot above because that's what an "object reference" is: A value, just like the value 5. It's just that the way that value is used is to look up where an object is. When you assign that value from one variable to another, the value gets copied, just like the value 5 gets copied when you copy it from one variable to another.

I can't say I like Eli's workaround much, I'd rather just be much more direct about it and use objects in the simple way:

var obj = {
    shout: function() { alert("Hello!"); }
};
var dasarray = [ obj ];

obj.shout();                               // Hello!
dasarray[0].shout();                       // Hello!

obj.shout = function() { alert("World"); };

obj.shout();                               // World!
dasarray[0].shout();                       // World!
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer, that made it clear, but @EliGassert also provided a very nice workaround :)
@FTav: Glad that helped. Due respect to Eli, but I think there's a better way to do it. That's more complex than necessary. FWIW, I've given a more direct way above.
No qualms about your solution. It's clean (especially the new edits). +1 to your answer. My solution was very generic and doesn't serve a real-world scenario. I imagine another layer or two of abstraction, separating the dasarray from the caller. Otherwise, you could even get more direct and change global to dasarray and modify the functions directly in the array. But I imagine the real solution will be more complicated than the example given and solutions we've presented.
3

I agree with T.J.'s answer. However, if you wanted to do some kind of workaround, you could create a function that always does a "just in time" lookup from a cache of functions. See this fiddle

var getFunctionRef = function(funcName)
{
    return function() { global[funcName](); };
}

var global = { };
var arr = [];

global.shout = function() { alert('hello'); }

arr[0] = getFunctionRef('shout');

global.shout = function() { alert('world'); }

arr[0]();

It's a very basic example. There are more extensive ways of doing it. But this should demonstrate the concept.

1 Comment

Thank you Eli, T. J. explained in a very good way, why this is not really that much of a reference than passing by value, but your solution also pushed me in the right direction to solve my problem!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.