5

I want to pass a function reference "go" into another function "redefineFunction", and redefine "go" inside of "redefineFunction". According to Johnathan Snook, functions are passed by reference, so I don't understand why go() does not get redefined when I pass it into redefineFunction(). Is there something that I am missing?

// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction(fn) {
    fn = function(x) { return x * 3; };
}

// initial version of go()
function go(x) {
    return x;            
}

go(5); // returns 5

// redefine go()
go = function(x) {
     return x * 2;        
}

go(5); // returns 10

// redefine go() using redefineFunction()
redefineFunction(go);

go(5); // still returns 10, I want it to return 15

​ Or see my fiddle http://jsfiddle.net/q9Kft/

3
  • 5
    When objects are passed by reference, you have a reference to the object's properties, but you can't overwrite the original reference with a new object. Commented May 10, 2012 at 13:57
  • @zzzzBov: well, you do have a reference to the object itself (not just its properties), you just can't overwrite the object. Commented May 10, 2012 at 14:03
  • @josh3736, yea, essentially what I'm getting at is that you don't have a pointer to an object, you have the object. Commented May 10, 2012 at 14:07

8 Answers 8

9

Pedants will tell you that JavaScript is pure pass-by-value, but I think that only clouds the issue since the value passed (when passing objects) is a reference to that object. Thus, when you modify an object's properties, you're modifying the original object, but if you replace the variable altogether, you're essentially giving up your reference to the original object.

If you're trying to redefine a function in the global scope: (which is a bad thing; you generally shouldn't have global functions)

function redefineFunction(fn) {
    window[fn] = function() { ... };
}

redefineFunction('go');

Otherwise, you'll have to return the new function and assign on the calling side.

function makeNewFunction() {
    return function() { ... };
}

go = makeNewFunction();
Sign up to request clarification or add additional context in comments.

5 Comments

Assuming what you really wanted to do is replace a global function. If you want to replace some function elsewhere, you're kinda screwed unless you pass in a context as well. And if you want to replace a locally defined function...you're just screwed.
Thanks for the through explanation. JavaScript is a fun language to work in, I'm always learning something new. "you generally shouldn't have global functions" I never really considered this on my web pages. What is the ideal alternative to global functions ? Encapsulate them within an object? I've done some OO JavaScript, but found it cumbersome ... maybe I'm just too used to C#.
@Walter: JS OOP takes a bit of time to wrap your head around. The big thing to realize is that there are no classes. You can make constructor functions act a tiny bit like classes, and the language likes to encourage you to try to, but that illusion fades away the second you try to actually use them as such. Embrace the prototypal model and duck-typing and all the other fun dynamic stuff, and things start to make a lot more sense.
@WalterStabosz, my preferred approach is to wrap your code in an anonymous function, thereby restricting the scope of your functions/variables (think private members in a C# class). You can then export public functions to the global scope if needed. !function() { var private; function foo() { ... }; window.bar = function() { foo(private); }; }(); -- you can call bar() from anywhere, and it will have access to your private members.
...by the way, this is how jQuery is written. The entire library is inside a self-executing anonymous function (and there are many helper functions in there that you can't access from the outside), and it exports $. In your page, you don't have direct access to jQuery's internal variables and functions, but you may indirectly call them when you call $(...). In this way, jQuery protects its functions from being overwritten, should you declare a function with the same name as a jQuery function.
2

Nothing is "passed by reference" in JS. There are times that references are passed, but they're passed by value. The difference is subtle, but important -- for one thing, it means that while you can manipulate a referenced object pretty much at will, you can't reliably replace the object (read: alter the reference itself) in any way the caller will see (because the reference was passed by value, and is thus merely a copy of the original reference; attempting to reassign it breaks in the same way it would if the arg were a number or string).

Some cowboys will assume you're redefining a global function and mess with it by name to get around the limitations of pass-by-value, but that will cause issues the second you decide not to have globals all over the place.

The real solution: return the new function, and let the caller decide what to do with it. (I'd argue that redefining functions right out from under the code that uses them is a pretty bad design decision anyway, but eh. I guess there could be a reason for it...)

Comments

1

You can't modify the variable from inside the function, so the quick fix is to return the value and assign it outside the function, like this

// log() just writes a message to the text area
function log(message) {
    $('#output').val($('#output').val() + message + "\n");
}

// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction() {
   newvalue = function(x) { return x * 3; };
   return newvalue;
}

// initial version of go()
function go(x) {
    return x;            
}

log(go(5)); // returns 5

// redefine go()
go = function(x) {
     return x * 2;        
}

log(go(5)); // returns 10

// redefine go() using redefineFunction()
go = redefineFunction();

log(go(5)); // returns 10, I want it to return 15

Comments

1

I believe functions are 'passed in by value'. If you put log(f(5)); inside your redefineFunction function it will output 15, but 10 when you call log(go(5)) afterwards.

If you change redefineFunction to return the function and then assign it to go (go = redefineFunction()) it will work as you expect.

Comments

1

This is equivalent to asking if you can redefine any variable by passing it as an argument to some function. No. You can reassign it by, uhh, reassigning it. In this case, if you make redefineFunction return a function, you can simply assign it to go:

function redefineFunction() {
    var fn = function(x) { return x * e; };
    return fn;
}

function go(x) {
    return x;
}

go = redefineFunction();

go(5); // return 15

Comments

1

This is working in firefox:

function redefineFunction(fn) {
    window[fn] = function(x) {
        return x * 3;
    }
};

function go(x) {
    return x;
};

alert(go(5));

go=function(x) {
    return x * 2;
}

alert(go(5));

redefineFunction('go');

alert(go(5));


The secret is that a global function called go also is called window.go and window["go"].
This can also be used at styles: element.style["overflow"] = "hidden", and in attributes:
element["value"] = "hello there". This is a very useful knowlege.

1 Comment

Assuming you want to replace a global function. If you try var x = function() { }; redefineFunction('x'); anywhere but in global scope (where it shouldn't really even be), you're in for a bit of a shock.
1

Why dont use a object? something like this:

var o = {
    go: function( x ) {
        return x;
    },

    redefineFunction: function ( fn ) {
        if (typeof fn === 'function') {
            this.go = fn;
        }
    }
}

console.log(o.go(5)); // return 5

var fn = function (x) {
  return x * 2;
};

o.redefineFunction(fn);

console.log(o.go(5));​ //return 10

Hope it helps!

Comments

1

Snook is wrong. And I don't think it's pedantic at all (@josh3736 :) to point out that EVERYTHING in JavaScript is pass by value. The article by Snook gets this COMPLETELY wrong. Passing a primitive and passing an object work the exact same way. These are equivalent:

var x = 2;
var y = x;
y = 3; //x is STILL 2.

function stuff(y){
  y = 3; //guess what. x is STILL 2
}

stuff(x);

///////////////////

var x = {stuff: 2};
var y = x;
y = {stuff: 3}; //x.stuff is STILL 2

function stuff(y){
  y = {stuff: 3}; //guess what. x.stuff is STILL 2
}

stuff(x);

This is important. Java, C#, and MOST languages work this way. That's why C# has a "ref" keyword for when you really do want to pass something by reference.

1 Comment

I think it is important to mention that if you wrote y.stuff = 3 inside the stuff function, x.stuff would be 3. In your example, you are creating a new object. I think this is the reason some people are confused with the whole value/reference concept.

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.