【问题标题】:Need to understand Javascript object references [duplicate]需要了解 Javascript 对象引用 [重复]
【发布时间】: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 - 我不认为你是对的。 stackoverflow.com/questions/13104494/…
  • “引用的副本”很好解释,但是副本仍然是作为值传递的,没有真正的引用,只是副本。

标签: javascript


【解决方案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 解释器的方式试运行执行:

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

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

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

    【讨论】:

      猜你喜欢
      • 2017-03-06
      • 2012-02-28
      • 2017-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      相关资源
      最近更新 更多