You need to understand Javascript object references
I am reviewing this piece of code from John Resig's site. I don't understand, when ninja object is set to empty object, yell method is still available for samurai.
Is it because since there is still a link lying around the ninja, this is not garbage collection?
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 bit to remove the named function expression).
source to share
In the following code:
var ninja = {
yell: function(n){
return n > 0 ? yell(n-1) + "a" : "hiy";
}
};
The ninja.yell value is a function reference. Purpose:
var samurai = { yell: ninja.yell };
Assigns a value to samurai.yell, which is a reference to the same function (i.e., referenced by ninja.yell). Then:
ninja = {};
Assigns the value to ninja, which is a new, empty object. It does not affect the value assigned to samurai.yell, which still references the function.
Variables have a value, values ββhave a Type . There is a special type, Reference Type , which "... is used to explain the behavior of operators such as delete, typeof, and assignment operators." Therefore, when an object is in an expression, the assigned value is the Reference Type.
Hence, the variable still has a value, but that value is a reference.
source to share
Break down the anonymous function referenced by the "yell" property for "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 "yell" function doesn't get "removed" when you reassign "ninja".
When you do:
var samurai = { yell: ninja.yell };
You assign any links to ninja.yell (what function yell()
} to "samurai.yell".
source to share
It's important to look at the original unmodified source for this:
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 the wrong change to script.
I don't understand, when ninja object is set to empty object, yell method is still available for samurai.
It's important to understand how assignment works in JavaScript. JavaScript has many handy shorthand conventions that can be difficult to understand when you are new to the language.
var samurai = { yell: ninja.yell };
This is a shorthand way of saying:
var samurai;
samurai = new Object();
samurai.yell = ninja.yell;
When called samurai.yell = ninja.yell
, a function reference is ninja.yell
appended to samurai
.
In JavaScript, functions are also objects. They are transmitted by reference. What samurai.yell = ninja.yell
does not copy any link to ninja
.
Line 9 of the example does not change the function in any way . It also does not modify the object that has been saved in any way. What it does is remove the reference to the object that was stored in and replace it with the value . This means that any other copy of the referenced object will still point to the object the ninja was referring to. var ninja = null
ninja.yell
ninja
ninja
null
ninja
It's easier to see 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 link lying around the ninja, this is not garbage collection?
After running line 9 of the example script, there are no more references to the object that was in ninja
. There is a link to the function that was in ninja.yell
. This means that an object ninja
can be garbage collected, but an object ninja.yell
(which is a function) cannot.
source to share
Run execution in such a way that the JavaScript interpreter:
- Create a new anonymous empty object (denoted as
obj1
). - Create a new anonymous function named
func1
that doesreturn n > 0 ? ...
. - Add a new property of this object
obj1
calledyell
, which is an alias for the functionfunc1
. - Assign a reference to this object to a new property called root
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
- Create a new anonymous empty object (denoted as
obj2
). - Add a new property to this object
obj2
calledyell
which is an alias for a functionfunc1
- not an alias forninja.yell
- the function reference "itself is copied intoobj2.yell
instead of a function reference. - Assign
obj2
tosamurai
. - Create a new anonymous empty object (denoted as
obj3
). - Assign a link 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
- A call
samurai.yell
that will lead to dereferencingobj2
and thenfunc1
to successfully complete the call.
source to share