将对象分配给未定义和删除之间的区别

时间:2016-12-01 10:02:30

标签: javascript javascript-objects

我正在阅读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仍然有一个对函数的引用,垃圾收集不会出现并在内存中删除它。

4 个答案:

答案 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 ,一切仍然正常工作。

因此,你可以忘记在我的问题中我所说的关于“引用”和“删除”的所有内容,它们不相关而且不真实。