需要了解 Javascript 对象引用 [重复]

Posted

技术标签:

【中文标题】需要了解 Javascript 对象引用 [重复]【英文标题】:Need to understand Javascript object references [duplicate] 【发布时间】:2015-01-12 11:52:34 【问题描述】:

我正在查看 John Resig 网站上的这段代码。我不明白的是,当 ninja 对象设置为空对象时,yell 方法仍然对 samurai 可用。

是不是因为仍然有一个对 ninja 的引用,所以它没有被垃圾收集?

var ninja = 
  yell: function(n)
    return n > 0 ? yell(n-1) + "a" : "hiy";
  
;


var samurai =  yell: ninja.yell ;

ninja = ;

console.log(samurai.yell(2)); //hiy

http://ejohn.org/apps/learn/#14(原创,我稍作修改,去掉了命名函数表达式)。

【问题讨论】:

ninja.yell 不是通过引用分配的,而是复制的。 不,实际上 javascript 只是按值传递 @adeneo,这不太正确,ninja.yellsamurai.yell 将共享对同一个函数对象的引用,问题是 ninja 正在被一个新对象完全覆盖。 @adeneo - 我不认为你是对的。 ***.com/questions/13104494/… “引用的副本”很好解释,但是副本仍然是作为值传递的,没有真正的引用,只是副本。 【参考方案1】:

在以下代码中:

var ninja = 
  yell: function(n)
    return n > 0 ? yell(n-1) + "a" : "hiy";
  
;

ninja.yell 的值是对函数的引用。作业:

var samurai =  yell: ninja.yell ;

samurai.yell 分配一个值,该值是对同一函数的引用(即由 ninja.yell 引用的那个)。那么:

ninja = ;

ninja 分配一个值,该值是一个新的空对象。它对分配给 samurai.yell 的值没有影响,它仍然引用该函数。

变量有值,值有Type。有一个特殊的类型称为Reference Type,它是“...用于解释诸如 delete、typeof 和赋值运算符的行为”。所以当一个对象在assignment expression中时,赋值为Type Reference。

因此变量仍然有一个值,但它的值是一个引用。

【讨论】:

我认为值得注意的是samurai.yell 指的是分配给ninja.yell函数,而不是ninja.yell 本身。您可以直接更改ninja.yell 的值(而不只是吹走ninja 对象)而不影响samurai.yell 这很好地总结了它。我只想补充一点,垃圾收集器将处理的唯一事情是那些不再在任何地方引用的事情。 不是同一个函数的引用,是原函数的copy,所以没有删除 无论如何,这个问题是Does JavaScript pass by reference?的副本 @adeneo——不,是同一个功能。函数是对象,它们不会在赋值中复制。您可能会说该值是复制的,因此它是引用的副本。它不是对象的副本。【参考方案2】:

重要的是查看这个原始未修改的来源:

 1. | var ninja =  
 2. |   yell: function(n) 
 3. |     return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
 4. |    
 5. | ; 
 6. | assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 
 7. |  
 8. | var samurai =  yell: ninja.yell ; 
 9. | var ninja = null; 
10. |  
11. | try  
12. |   samurai.yell(4); 
13. |  catch(e) 
14. |   assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
15. | 

您将ninja.yell 更改为yell 的更改是对脚本的无效编辑。

我不明白的是,当忍者对象设置为空对象时,yell方法仍然可供武士使用。

了解赋值在 JavaScript 中的工作原理很重要。 JavaScript 有许多方便的速记符号,当您不熟悉该语言时,这些符号会使您更难理解。

var samurai =  yell: ninja.yell ;

是一种简写方式:

var samurai;
samurai = new Object();
samurai.yell = ninja.yell;

samurai.yell = ninja.yell被调用时,ninja.yell函数的引用被添加到samurai

在 JavaScript 中,函数也是对象。它们通过引用传递。 samurai.yell = ninja.yell 不做是复制任何对ninja 的引用。

在示例的第 9 行,var ninja = null 不会以任何方式修改 ninja.yell 处的函数。它也不会以任何方式修改存储在ninja 的对象。它所做的是删除对存储在ninja 的对象的引用,并将其替换为null 的值。这意味着 ninja 引用的对象的任何其他副本仍将指向 ninja 引用的对象。

举个例子更容易理解:

var foo,
    bar;

foo = 
    fizz: 'buzz'
;
bar = foo;
foo = null;
console.log(bar.fizz); //buzz

是不是因为仍然有一个对 ninja 的引用,所以它没有被垃圾收集?

在示例脚本的第 9 行执行后,不再有 任何 对位于 ninja 的对象的引用。 ninja.yell 处的函数的引用。这意味着ninja 对象可以被垃圾回收,但ninja.yell 对象(恰好是一个函数)不能。

【讨论】:

@RobG,复制的引用是恰好位于ninja.yell 的函数。没有复制对忍者的引用,这就是我所说的。【参考方案3】:

以 JavaScript 解释器的方式试运行执行:

    创建一个新的匿名空对象(标识为obj1)。 创建一个名为 func1 的新匿名函数,该函数执行 return n > 0 ? ...。 向该对象obj1 添加一个名为yell 的新属性,它是函数func1 的别名。 将此对象的引用分配给名为ninja 的新根属性(变量可以视为字段属性)。

此时,“堆”看起来像这样:

func1 = n => return n > 0 ? yell(n-1) + "a" : "hiy"
obj1  =  yell: func1 
ninja = obj1
    创建一个新的匿名空对象(标识为obj2)。 向该对象obj2 添加一个名为yell 的新属性,它是函数func1 的别名 - 不是 ninja.yell 的别名 - 对“函数值”的引用" 本身被复制到 obj2.yell 而不是一个引用到一个引用到一个函数。 将obj2 分配给samurai。 创建一个新的匿名空对象(标识为obj3)。 将此对象的引用分配给ninja

此时,“堆”看起来像这样:

func1   = n => return n > 0 ? yell(n-1) + "a" : "hiy"
obj1    =  yell: func1  // this no-longer has any references and will be GC'd at some point
obj2    =  yell: func1 
obj3    = 
samurai = obj2
ninja   = obj3
    调用samurai.yell 将取消引用obj2 然后func1 以成功拨打电话。

【讨论】:

【参考方案4】:

破解'ninja'的'yell'属性引用的匿名函数:

function yell(n) 
  return n > 0 ? yell(n-1) + "a" : "hiy";


var ninja = 
  yell: yell
;

现在可以更容易地看到,当您重新分配“ninja”时,函数“yell”并没有被“删除”。

当你这样做时:

var samurai =  yell: ninja.yell ;

您将任何 ninja.yell 引用(即 function yell() 分配给“samurai.yell”。

【讨论】:

以上是关于需要了解 Javascript 对象引用 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

在javascript中分配值而不是引用[重复]

一篇文章带你了解JavaScript中的变量,作用域和内存问题

在 JavaScript 原型函数中保留对“this”的引用 [重复]

JavaScript 对象的“值是引用”是啥意思

JavaScript中的对象比较[重复]

访问 Javascript 对象原型 [重复]