作为原始的布尔值和作为对象属性的布尔值有什么区别?

What's the difference between a boolean as primitive and a boolean as property of an object?

提问人:Mark Rensen 提问时间:2/5/2017 更新时间:4/6/2018 访问量:609

问:

我正在遵循一些画布教程。下面的代码是其中的一个片段。

在这个片段中,他们为什么不选择成为一个简单的布尔值?我认为该语句无论如何都会起作用,但是当我尝试更改代码以使用布尔值时,代码不起作用。runAnimationx = !x

那么,作为原始的布尔值和作为对象属性的布尔值有什么区别呢?

   /*
   * define the runAnimation boolean as an object
   * so that it can be modified by reference
   */
  var runAnimation = {
    value: false
  };

  // add click listener to canvas
  document.getElementById('myCanvas').addEventListener('click', function() {
    // flip flag
    runAnimation.value = !runAnimation.value;
javascript html5-canvas

评论


答:

8赞 Scott Marcus 2/5/2017 #1

在 JavaScript 中,所有参数都由“value”传递。这意味着,当传递参数时,将传递变量中存储的内容的副本。

基元(如布尔值)存储它们所表示的实际数据,因此,当传递基元时,会发送数据的副本,从而产生两个数据副本。对一个的更改不会影响另一个。

但是,当您将对象分配给变量时,该变量存储的是可以找到该对象的内存位置,而不是对象本身。将对象作为参数传递会导致传递内存地址的副本。在这些情况下,您最终可能会得到两个存储相同内存地址的变量,因此无论您使用哪个变量,都会影响同一个基础对象。

在您的场景中,您当然可以仅使用布尔变量使其工作,但本教程似乎希望将其封装到一个对象中,以便布尔数据的副本不会四处浮动,并且意外更改一个变量而不是另一个变量的可能性较小。

下面是一些基本示例:

// This function takes a single argument, which it calls "input"
// This argument will be scoped to the function.
function foo(input){
  // The function is going to alter the parameter it received
  input = input + 77;
  console.log(input); 
}

var input = 100;  // Here's a higher scoped variable also called "input"

foo(input);       // We'll pass the higher scoped variable to the function

// Now, has the higher level scoped "input" been changed by the function?
console.log(input);  // 100 <-- No, it hasn't because primitives pass a copy of their data

// ************************************************

// Now, we'll do roughly the same thing, but with objects, not primitives
function foo2(obj){
  obj.someProp = 100;
  console.log(obj.someProp);
}

var obj = {
  someProp : 50
};

foo2(obj);

// Here, we see that the original object has been changed by the funciton we passed it to
console.log(obj.someProp);

评论

0赞 Mark Rensen 2/5/2017
谢谢,我很高兴看到我的想法是正确的,但还没有完全到位。你的例子说得很清楚。这就像在数学中一样,我们用()来欺骗计算的顺序。在 JS 中,我们可以使用对象来欺骗作用域/按值传递。
0赞 Mark 2/19/2021
你说,“基元(如布尔值)存储它们所表示的实际数据,因此,当传递基元时,会发送数据的副本,从而产生两个数据副本。字符串也是原语 - 这是否意味着传递一个字符串会导致一个字符串的两个副本?
0赞 Scott Marcus 2/19/2021
@Mark 如果你传递一个字符串原语,是的。但是字符串也有一个对象类型,所以这取决于你要处理的,字符串与字符串。
0赞 Mark 2/19/2021
@ScottMarcus 我说的是字符串基元类型。因此,如果我有一个 1000 万个字符的原始字符串并将其作为参数传递给函数,那么现在会有两个字符串在内存中重复数百万字节?这不是效率低吗?
0赞 Scott Marcus 2/19/2021
@Mark 正如我所说,“如果你传递一个字符串原语,是的”。在你的例子中,对于这样的字符串,你应该创建一个 String 对象(即 ),以便你只将对象引用的副本传递给内存中的对象,而不制作实际字符串数据的副本。let s = new String("string content here");