我正在阅读JavaScript Ninja的秘密,并遇到了一个编码示例:
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
var samurai = { yell: ninja.yell };
//var ninja = {};
//delete ninja;
samurai.yell(4);
如果我取消注释第二个注释行,samurai.yell(4)
运行正常,我理解为samurai
仍然引用ninja.yell
最初引用的函数,delete
除去。
但是,如果我改为运行第一条注释行,samurai.yell(4)
将会出错。
有人可以解释一下在幕后发生的事情吗?
我认为如果您将ninja
指定为undefined
,则会创建一个新引用并链接到ninja
,原始引用仍然存在,并且samurai
仍然有一个对函数的引用,垃圾收集不会出现并在内存中删除它。
答案 0 :(得分:3)
写作时
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
var samurai = { yell: ninja.yell };
var ninja = {};
//delete ninja;
samurai.yell(4);
var ninja = {};
将声明一个名为ninja
下一行samurai.yell(4);
将抛出一个错误,因为ninja已被重新定义,并且新对象没有任何与之关联的名为yell
的函数。
说明:
一开始,ninja
是
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
这里的忍者在其中有一个函数yell
,可以调用它。
将其值更改为
时ninja = {};
旧值(连同函数)被删除并用新值覆盖。
因此,当您致电
时samurai.yell(4);
它转到{ yell: ninja.yell };
,由于新ninja
没有yell
函数,因此会抛出错误
答案 1 :(得分:3)
正如我在评论中所说的那样,关闭闭包的工作原理:
var bar = 1;
var foo = function() { console.log(bar); } // closes over `bar`
foo(); // closed-over `bar` reference: 1
bar = 2;
foo(); // closed-over `bar` reference reflects the update: 2
delete bar;
foo(); // closed-over `bar` still references the same location,
// even when current context no longer has it
您不经常看到delete
的示例,但正是这种机制使它们可以替代私有变量:
var foo = (function() {
var bar = 1;
return function() {
console.log(bar);
};
})();
foo(); // closed-over `bar`: 1 - even though:
console.log(bar); // Uncaught ReferenceError, as
// `bar` is not available in local context
答案 2 :(得分:0)
是的,你理解正确,但真正的原因是GC机制。
他们正在传递对函数本身的引用,ninja.yell。这是因为垃圾收集机制 - 直到任何引用都存活,这个函数将不会被收集。
在这种特殊情况下,我们抓取对ninja.yell
的引用,即使我们删除最初包含此函数的对象,也不会进行垃圾回收。
大多数情况下,这种行为是不受欢迎的,这就是人们通常会避开delete
运算符的原因。
答案 3 :(得分:0)
我检查了@mrid和@Amadan的答案,并想了一下,我想我已经弄明白并提出了一个更简单的答案,我在我的问题中的解释并不完全正确。
如果我取消注释第一个注释行,
var ninja = {};
我正在将ninja分配给一个空对象,因此,我尝试运行“samurai.yell(4);”,我收到一个错误。原因是因为 samurai 试图通过其“范围链”找到 ninja (即一些封闭魔法,它确实找到了它,但是当它试图找到它时如果 ninja 有一个属性叫喊,它就会消失,因此错误。虽然 samurai 仍然保留对该匿名函数的引用(这意味着它仍然可以运行该功能),当匿名函数试图运行ninja.yell()
时,它再也找不到它,因此错误。
解决此问题的方法当然是使用this
,
var ninja = {
yell: function (n) {
return n > 0 ? this.yell(n-1) + "a" : "hiy";
}
};
var samurai = { yell: ninja.yell };
var ninja = {};
samurai.yell(4);
另一种方法是使用命名函数表达式
var ninja = {
yell: function foo(n) {
return n > 0 ? foo(n-1) + "a" : "hiy";
}
};
var samurai = { yell: ninja.yell };
var ninja = {};
samurai.yell(4);
一切都应该有效。
如果我取消注释第二个注释行,
delete ninja;
我实际上什么也没做。 删除仅用于“删除”对象的属性(您永远不能删除Javascript中的任何内容,垃圾收集处理),如果操作成功则返回true,否则返回false。因此,“删除”不适用于 ninja ,所以简单来说,当我取消注释“删除忍者”时,我没有做任何额外的 ninja ,一切仍然正常工作。
因此,你可以忘记在我的问题中我所说的关于“引用”和“删除”的所有内容,它们不相关而且不真实。