需要了解 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.yell
和 samurai.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中的变量,作用域和内存问题