why does the named javascript function persist?

71 views Asked by At

This is an adaptation of what you'd find in john resig's Learning Advanced Javascript app.

var math = {
    fact: function fact(n){
        return n > 0 ? n * fact(n-1): 1;
    },
    fact1: function (n) {
        return n > 0? n * math.fact1(n-1) : 1;
    }
};

console.log(math.fact(5));  // 120
console.log(math.fact1(5)); // 120

var o = {
    x: math.fact,
    y: math.fact1
};

math = {};

console.log(o.x === undefined); // false
console.log(o.y === undefined); // false
console.log(o.x(5));            // 120
console.log(o.y(5));            // Uncaught TypeError: math.fact1 is not a function

One would expect o.x(5) should throw an error, but it executes. Why?

3

There are 3 answers

3
JLRishe On BEST ANSWER

When an object literal expression is evaluated, the expression to the right of each colon is evaluated and assigned to the specified property.

So when this executes:

var o = {
    x: math.fact,
    y: math.fact1
};

the expression math.fact is evaluated, and the result is the function that math.fact is referring to at that time.

The same goes for math.fact1.

So even if you reassign the math variable with math = {}. o.x will continue to refer to that function. The o object has no knowledge of the math variable.


The reason why o.y(5) throws an error has to do with closures.

o.y refers to this function:

function (n) {
     return n > 0? n * math.fact1(n-1) : 1;
}

You can see here that it uses the math variable. Even if you reassign the math variable, this function will continue to refer to the variable itself.

When you call o.y(5), the function executes, but when it tries to call math.fact(n-1), that fails, because math no longer has a property called fact.


o.x does not have the problem that o.y has. It is a named function, and therefore is able to continue to call itself even after math is reassigned.

1
Niet the Dark Absol On

I don't understand why you would expect an error on o.x.

At the start, when we define math = { ... }, we have two functions.

The first function is referenced both by its name fact, and by the object property math.fact

The second function is anonymous, and is referenced only by math.fact1

We then "copy" the functions into o. At this point, the first function is referenced by all of fact, math.fact and o.x, while the second function is referenced by math.fact1 and o.y.

We then destroy the math object by replacing it with a new, empty object. At this point, the first function is referenced by fact and o.x, and the second only by o.y.

Now we actually call the functions. o.x contains a recursive call to fact, which is very clearly still defined as that function. However, o.y contains a recursive call to math.fact1, which we destroyed, hence an error there.

This has nothing to do with... well, anything of interest. The function that names something that still exists continues to work, while the function that names something that doesn't exist any more fails.

I mean... duh...

0
helpermethod On

o.x is a reference to a function (object). Because of this reference, the fact function will keep existing after the math object has been reset (objects only get garbage collected when no reference to them exist anymore).