这是对你在john resig的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
人们会期望o.x(5)
应该抛出一个错误,但它会执行。为什么呢?
答案 0 :(得分: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
的属性。< / p>
<小时/>
o.x
没有o.y
的问题。它是一个命名函数,因此即使在重新分配math
之后也可以继续调用它。
答案 1 :(得分:3)
我不明白为什么你会在o.x
上发现错误。
一开始,当我们定义math = { ... }
时,我们有两个函数。
第一个函数的名称为fact
的和,以及对象属性math.fact
第二个函数是匿名函数,仅由math.fact1
然后我们将这些功能“复制”到o
。此时,第一个函数由fact
,math.fact
和o.x
引用,而第二个函数由math.fact1
和o.y
引用。< / p>
然后我们通过用一个新的空对象替换它来销毁math
对象。此时,第一个函数由fact
和o.x
引用,第二个函数仅由o.y
引用。
现在我们实际上调用了这些函数。 o.x
包含对fact
的递归调用,该调用非常明确地定义为该函数。但是,o.y
包含对math.fact1
的递归调用,我们将其销毁,因此出现错误。
这与任何感兴趣的东西无关。命名仍然存在的东西的函数继续工作,而命名不存在的东西的函数失败。
我的意思是......呃...
答案 2 :(得分:1)
o.x
是对函数(对象)的引用。由于这个引用,在重置数学对象之后,事实函数将保持存在(对象只在不再存在对它们的引用时才会收集垃圾)。