4

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/

Walter Stabosz
  • 7,447
  • 5
  • 43
  • 75
  • 4
    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. – zzzzBov May 10 '12 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. – josh3736 May 10 '12 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. – zzzzBov May 10 '12 at 14:07

8 Answers8

8

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();
Community
  • 1
  • 1
josh3736
  • 139,160
  • 33
  • 216
  • 263
  • 1
    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. – cHao May 10 '12 at 14:03
  • 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 Stabosz May 10 '12 at 14:32
  • 1
    @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. – cHao May 10 '12 at 14:44
  • @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. – josh3736 May 10 '12 at 16:40
  • ...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. – josh3736 May 10 '12 at 16:44
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...)

cHao
  • 84,970
  • 20
  • 145
  • 172
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.

aquinas
  • 23,318
  • 5
  • 58
  • 81
  • 1
    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. – Bruno Silva May 10 '12 at 14:26
0

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
evilpenguin
  • 5,448
  • 6
  • 41
  • 49
0

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.

Mitch Satchwell
  • 4,770
  • 2
  • 24
  • 31
0

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
0

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.

  • 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. – cHao May 10 '12 at 14:28
0

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!

Juan Leung
  • 346
  • 4
  • 13