4

I am looking at this piece of code from John Resig's website. What I don't understand is when the ninja object is set to an empty object, the yell method is still available to samurai.

Is it because since there is still a reference lying around to ninja, it wasn't garbage collected?

var ninja = {
  yell: function(n){
    return n > 0 ? yell(n-1) + "a" : "hiy";
  }
};


var samurai = { yell: ninja.yell };

ninja = {};

console.log(samurai.yell(2)); //hiy

http://ejohn.org/apps/learn/#14 (Original source, I modified it a little to remove the named function expression).

learner
  • 61
  • 3
  • 4
    `ninja.yell` isn't assigned by reference, it's copied. – Madbreaks Nov 15 '14 at 00:22
  • Why is this passed by value rather than reference? I thought all objects were passed by reference? – learner Nov 15 '14 at 00:23
  • 1
    can you link to the original source? `yell(n-1)` doesn't reference any function in the snippet you've provided. – zzzzBov Nov 15 '14 at 00:24
  • 2
    Nope, in reality javascript only passes by value – adeneo Nov 15 '14 at 00:24
  • .. but for objects it's a copy of a value – adeneo Nov 15 '14 at 00:24
  • 5
    @adeneo, that's not quite correct, `ninja.yell` and `samurai.yell` will share a reference to the same function object, the problem is that `ninja` is being overwritten with a new object completely. – zzzzBov Nov 15 '14 at 00:25
  • 2
    @adeneo - i don't think you're right. http://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference – learner Nov 15 '14 at 00:28
  • 1
    @learner that question answers this question perfectly. – Evan Davis Nov 15 '14 at 00:29
  • 4
    "copy of a reference" is a good way to explain it, but the copy is still passed as a value, there are no true references, just copies. – adeneo Nov 15 '14 at 00:29
  • Passing by reference means it's possible to pass a variable in such a way as to allow a function to modify the value of that variable in the calling context. In other words, it creates an alias, a reference, to the variable in the calling context, and javascript has no such thing. JavaScript just creates a local variable inside the function with the same reference value as the variable passed in, or a copy of the value passed in from the calling context so to speak, hence it really only has pass-by-value. – adeneo Nov 15 '14 at 00:37
  • @zzzzBov no they don't; `ninja.yell` can be updated independently without affecting `samurai.yell`, because in the case of a method on an object, the method is copied. – Evan Davis Nov 15 '14 at 00:38
  • 1
    @learner I just noticed that you are the OP _and_ you posted a link to IMO an exact duplicate of this question. – Evan Davis Nov 15 '14 at 00:42
  • @Mathletics, "`ninja.yell` can be updated independently without affecting `samurai.yell`" [nope, you're wrong about that one](http://jsfiddle.net/tnbs0z11/). – zzzzBov Nov 15 '14 at 00:46
  • 1
    @zzzzBov point taken, but you're [wrong about which part i was wrong about](http://jsfiddle.net/tnbs0z11/1/). Updating `ninja.yell` to refer to a new function can be done without affecting `samurai.yell`; adding properties to `ninja.yell` DOES affect `samurai.yell` because it is the same object. The latter is the part I had wrong. Your fiddle demonstrates this, but the part of my comment that you quoted is, indeed, correct. – Evan Davis Nov 15 '14 at 00:56
  • @learner this might help you understand too ([**fiddle**](http://jsfiddle.net/tivie/1wk5eoe9/)) – Tivie Nov 15 '14 at 01:13

4 Answers4

5

In the following code:

var ninja = {
  yell: function(n){
    return n > 0 ? yell(n-1) + "a" : "hiy";
  }
};

The value of ninja.yell is a reference to a function. The assignment:

var samurai = { yell: ninja.yell };

Assigns a value to samurai.yell that is a reference to the same function (i.e. the one referenced by ninja.yell). Then:

ninja = {};

Assigns a value to ninja that is a new, empty object. It has no effect on the value assigned to samurai.yell, which still references the function.

Variables have a value, values have a Type. There is a special Type called the Reference Type that is "…used to explain the behaviour of such operators as delete, typeof, and the assignment operators". So when an object is in an assignment expression, the value assigned is Type Reference.

Hence the variable still has a value, but it's value is a reference.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • 2
    I think it's worth noting that `samurai.yell` refers to the _function assigned to `ninja.yell`_, not to `ninja.yell` itself. You can change the value of `ninja.yell` directly (not just blowing away the `ninja` object) without affecting `samurai.yell` – Evan Davis Nov 15 '14 at 00:40
  • This sums it up nicely. I would only add that the only things that will be handled by the garbage collector are those things which are no longer referenced anywhere. – Gabriel de Oliveira Nov 15 '14 at 00:40
  • 1
    It's not a reference to the same function, it's a **copy** of the original function, which is why it's not deleted – adeneo Nov 15 '14 at 00:42
  • In any case, this question is a duplicate of [Does JavaScript pass by reference?](http://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference) – Evan Davis Nov 15 '14 at 00:43
  • @Mathletics—I think that's what "*Assigns a value to samurai.yell that is a reference to the same function*" says, but I'll add some words. – RobG Nov 15 '14 at 00:45
  • 1
    @adeneo—no, it's the same function. Functions are Objects, they are not copied in an assignment. You might say the value is copied, so it's a copy of the reference. It's not a copy of the object. – RobG Nov 15 '14 at 00:46
  • Okay, we're basically talking about the same thing, I call it a copy of a value as references doesn't really exists, but it's not a true copy, as in `ninja.yell != samurai.yell` etc. – adeneo Nov 15 '14 at 00:52
1

Break out the anonymous function which is referenced by 'yell' property of 'ninja':

function yell(n) {
  return n > 0 ? yell(n-1) + "a" : "hiy";
}

var ninja = {
  yell: yell
};

Now it's a little easier to see that the function 'yell' does not get 'deleted' when you reassign 'ninja'.

When you do:

var samurai = { yell: ninja.yell };

You assign whatever ninja.yell references (which is function yell()} to 'samurai.yell'.

lxe
  • 7,051
  • 2
  • 20
  • 32
0

It's important to look at the original unmodified source for this one:

 1. | var ninja = { 
 2. |   yell: function(n){ 
 3. |     return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
 4. |   } 
 5. | }; 
 6. | assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 
 7. |  
 8. | var samurai = { yell: ninja.yell }; 
 9. | var ninja = null; 
10. |  
11. | try { 
12. |   samurai.yell(4); 
13. | } catch(e){ 
14. |   assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
15. | }

The change you made to change ninja.yell to yell is an invalid edit to the script.

What I don't understand is when the ninja object is set to an empty object, the yell method is still available to samurai.

It's important to understand how assignment works in JavaScript. JavaScript has a lot of convenient shorthand notations that can make things harder to understand when you're new to the language.

var samurai = { yell: ninja.yell };

Is a shorthand way of saying:

var samurai;
samurai = new Object();
samurai.yell = ninja.yell;

When samurai.yell = ninja.yell is called, a reference to the ninja.yell function is added to samurai.

In JavaScript, functions are objects too. They are passed by reference. What samurai.yell = ninja.yell does not do is copy any sort of reference to ninja.

On line 9 of the example, var ninja = null does not modify the function at ninja.yell in any way. It also does not modify the object that was stored at ninja in any way. What it does is remove the reference to the object that was stored at ninja, and replace it with a value of null. This means that any other copy of the object referenced at ninja will still point to the object that was referenced at ninja.

It's easier to see with an example:

var foo,
    bar;

foo = {
    fizz: 'buzz'
};
bar = foo;
foo = null;
console.log(bar.fizz); //buzz

Is it because since there is still a reference lying around to ninja, it wasn't garbage collected?

After line 9 of the example script executes, there are no longer any references to the object that had been at ninja. There is a reference to the function that had been at ninja.yell. What this means is that the ninja object can be garbage collected, but the ninja.yell object (which happens to be a function) cannot.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • @RobG, the reference copied is of the function that happens to be at `ninja.yell`. No reference to ninja is copied, which is what I said. – zzzzBov Nov 15 '14 at 01:11
0

Dry-run the execution the way that a JavaScript interpreter would:

  1. Create a new, anonymous, empty object (identified as obj1).
  2. Create a new, anonymous, function called func1 which does return n > 0 ? ....
  3. Add a new property to this object obj1 called yell which is an alias for the function func1.
  4. Assign a reference to this object to a new root property called ninja (variables can be thought of as field properties).

At this point, the "heap" looks like this:

func1 = n => return n > 0 ? yell(n-1) + "a" : "hiy"
obj1  = { yell: func1 }
ninja = obj1
  1. Create a new, anonymous, empty object (identified as obj2).
  2. Add a new property to this object obj2 called yell which is an alias for the function func1 - not an alias for ninja.yell - a reference to the "function value" itself is copied into obj2.yell rather than a reference-to-a-reference-to-a-function.
  3. Assign obj2 to samurai.
  4. Create a new, anonymous, empty object (identified as obj3).
  5. Assign a reference to this object to ninja.

At this point, the "heap" looks like this:

func1   = n => return n > 0 ? yell(n-1) + "a" : "hiy"
obj1    = { yell: func1 } // this no-longer has any references and will be GC'd at some point
obj2    = { yell: func1 }
obj3    = {}
samurai = obj2
ninja   = obj3
  1. Call samurai.yell which will dereference obj2 and then func1 to successfully make the call.
Dai
  • 141,631
  • 28
  • 261
  • 374