Javascript:没有名称的对象会发生什么?

4
如果我在全局范围内执行以下操作:

var myObject = {name: "Bob"};

我有一种方式可以指向内存中的对象(即字符串标识符“myObject”)。我可以打开控制台并输入:myObject.name,控制台将会返回:

"Bob"

现在,如果我只是输入:

,那么:

{name: "Jane"};

我正在某处创建该对象,并猜测它会在某个作用域中继续存在。有没有办法我可以找到它?它是否存在于window下的某个通用存储位置?
编辑:有些人说它只会被垃圾回收。
那么这个例子怎么样呢?
var MyObject = function(){
    $("button").click(this.alert);
}

MyObject.prototype.alert = function(){
    alert("I heard that!")
}

new MyObject();

由于它的回调函数被绑定到了DOM事件上,因此它不能被垃圾回收。那么产生的对象在哪里存储,能否被访问?


不是的。该对象被创建并进行垃圾回收。就像那些"use strict";字符串一样。我认为一些JS引擎甚至可以避免创建它。 - MaxArt
有人之前提出了这个问题,但是他们已经删除了评论。你的第一个例子是否确实定义了你认为的对象,还是只是一个块、一个标签和一个字符串? - Martin Smith
1
我已经更新了我的答案,稍微扩展了一下你的更新(关于在构造函数中绑定事件处理程序,我可以补充一下,这通常不是最好的选择。) - Elias Van Ootegem
2个回答

5
如果没有任何指向该对象的引用(即您没有将其分配给任何变量或属性值),则无法访问它,事实上,它也不存在了,因为垃圾回收器可以立即回收该内存。

2
我喜欢想象它已经逃脱了,正在自由自在地生活。 - Yatrix
2
@Yatrix 我认为应该是“逃亡中”,但“生活在羊肉上”听起来更美味! - Matt Harrison
@MattHarrison 嘿嘿。我从来没有逃亡过,但如果我真的逃亡了,希望我有羊肉可以带上。 - Yatrix

2
短答案是不行,对象没有被保留在内存中无法访问的地方。但实际情况更加复杂,但只要掌握了基础知识就不算太难理解。
更新: 针对您的更新:从某种意义上说,您是正确的。回调是对 MyObject.prototype.alert 的引用,您使用 this.alert 访问了该引用,但该函数对象被构造函数的原型引用,因此无论如何也无法进行垃圾回收。实例本身与 alert 函数本身无关,因此可以安全地进行垃圾回收。
可以这样想:
MyConstructor.prototype.alert = 0x000123;//some memory address
   ||
   \/
0x000123 = [object Function];

函数对象本身并没有直接附加到任何东西上,它漂浮在内存中,并被原型引用。当您创建实例时:

new MyConstructor().alert;

解决方法如下:
[new MyConstructor instance] allocated at e.g 0x000321
   ||
   \\
    \=>[alert] check for alert @instance -> not found
          \\
           \=> check prototype, yield 0x000123 <-- memory address, return this value

所以在执行以下语句时:

 $("button").click(this.alert);
this.alert 是一个表达式,解析为 0x000123。换句话说,jQ 的 click 方法(函数对象)只接收 alert 函数对象的内存地址。实例或构造函数根本没有涉及。这就是为什么调用上下文或者说 this 可以根据函数被调用的方式和位置而改变。更多关于临时上下文确定的信息请参见此处
/*assume your code is here*/
new MyConstructor().alert = function(){ alert('I am deaf');};
MyConstructor.prototype.alert = 'foo';
$('#button').click();

猜猜看,点击事件弹出的警告信息“我听说过”,原型甚至没有涉及,更不用说实例了。
如果MyConstructor 超出范围,则click 事件仍将正常工作,因为GC仍然看到对警报函数对象的引用,该对象尚未超出范围。其他所有内容都可以进行GC,但是...


JS垃圾回收器(GC)是一种标志和扫描GC。当JS引擎遇到您的语句时,确实分配存储对象所需的内存。当达到下一个语句时,该对象很可能仍在内存中。
偶尔,GC会X检查它在内存中看到的所有对象,并尝试找到仍然可访问的该对象的所有引用。当它遇到刚刚创建的对象文本而没有分配引用的情况时,该对象将被标记为垃圾回收。
下次GC开始清除标记对象时,该对象将从内存中删除。

当然,这对于所有引擎来说并非完全正确。假设您的语句是在IIFE中编写的,并返回了一个函数:

var foo = function()
{
    {name: 'bar'};
    return function()
    {
        return 'foobar';
    };
}());

一些引擎会将整个IIFE的范围保留在内存中,只有当IIFE的返回值超出范围(被标记为GC)时才会释放该范围的内存。其他引擎(例如V8,我上次检查时是这样的)实际上会标记那些未被其返回值引用的外部范围的对象/变量。
不过,仔细想想,这可能并不适用于这种情况,因为即使在IIFE返回之前,GC也可能会启动...但总的来说,这只是吹毛求疵。

还有一个问题需要考虑:逻辑OR:

var name = (mayNotExist || {name:'default'}).name;

在这种情况下,如果mayNotExist确实存在,由于JS对表达式的短路求值,对象文字甚至不会被创建。
关于此事的一些链接:

非常感谢您提供的详尽答案。 - Matt Harrison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接