0

这是对您在 john resig 的 Learning Advanced Javascript 应用程序中找到的内容的改编。

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

人们期望o.x(5)应该抛出一个错误,但它会执行。为什么?

4

3 回答 3

5

当对象文字表达式被求值时,每个冒号右边的表达式被求值并分配给指定的属性。

所以当它执行时:

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

表达式math.fact被评估,结果是当时引用math.fact的函数。

也是如此math.fact1

因此,即使您使用 重新分配math变量math = {}o.x将继续引用该函数。o对象不知道math变量 。


o.y(5)抛出错误 的原因与闭包有关。

o.y指的是这个功能:

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

您可以在这里看到它使用了math 变量。即使您重新分配math变量,此函数仍将继续引用变量本身。

当你调用o.y(5)时,函数会执行,但是当它尝试调用math.fact(n-1)时,会失败,因为math不再有一个名为的属性fact


o.x没有那个问题o.y。它是一个命名函数,因此即使在重新分配后也能够继续调用自己math

于 2014-12-10T11:27:56.450 回答
3

我不明白为什么您会期望o.x.

一开始,当我们定义 时math = { ... },我们有两个函数。

第一个函数由其名称fact对象属性引用math.fact

第二个函数是匿名的,仅由math.fact1

然后我们将函数“复制”到o. 此时,第一个函数被所有fact,math.fact和引用o.x,而第二个函数被math.fact1and引用o.y

math然后我们通过用一个新的空对象替换它来销毁该对象。此时,第一个函数由factand引用o.x,第二个函数仅由引用o.y

现在我们实际调用函数。o.x包含对 的递归调用fact,它仍然非常清楚地定义为该函数。但是,o.y包含对 的递归调用math.fact1,我们将其销毁,因此出现错误。

这与......好吧,任何有趣的事情无关。命名仍然存在的东西的函数继续工作,而命名不再存在的东西的函数失败。

我的意思是……呃……

于 2014-12-10T11:32:36.540 回答
1

o.x是对函数(对象)的引用。由于这个引用,事实函数将在数学对象被重置后保持存在(对象只有在不再存在对它们的引用时才会被垃圾回收)。

于 2014-12-10T11:32:37.717 回答