【问题标题】:If all JavaScript types are objects, then why are numbers be passed by value?如果所有 JavaScript 类型都是对象,那么为什么数字是按值传递的?
【发布时间】:2015-08-14 20:48:42
【问题描述】:

在有关闭包的文章中,您经常会看到在循环内部创建闭包,使用自调用函数将迭代器变量传递给返回的函数表达式,以便在迭代器变量的值周围创建闭包。自调用函数被调用,而不是在循环结束后调用它的值。这是一个例子:

var func = [];

for (var i = 0; i < 3; i++)
{
    func.push((function(j){ return function(){ console.log(j); }; })(i));
}

// Logs 
// 0
// 1
// 2 
// to the console
for (var i = 0; i < func.length; i++)
{
    func[i]();
}

根据我的简单实验,这种技术适用于数字和字符串。但是,相同的技术不适用于纯 JavaScript 对象。如果将对象传递给自调用函数并在函数表达式中引用,则对封闭对象的更改在函数表达式中可见,因为传递给自调用函数的值是对对象的引用,而不是对象的副本对象,就像数字和字符串一样。

我可以理解为什么这种技术不适用于存储对象的变量,但我不明白为什么这种技术应该适用于数字和字符串,它们的原型链以 Object 终止。

这是否意味着字符串和数字只是解释器以不同方式处理的对象的特殊情况,还是我有一个基本的误解?

【问题讨论】:

  • 当您写“相同的技术不适用于纯 JavaScript 对象”时,您的意思并不清楚。它以什么方式“不起作用”?出乎意料的会发生什么?
  • @Pointy - 好问题。我的意思是,当一个对象在每次循环迭代中发生变异时,这些变异会反映在函数表达式已关闭的对象中。这正是我对一个对象的期望。我不明白(或不明白)为什么像数字和字符串这样的原型链以 Object 结尾的东西的行为方式不同。
  • 好的,那是因为你一遍又一遍地将完全相同的对象的引用推送到数组上。它实际上与按值传递与按引用传递无关。对象的“值”是对该对象的引用;这是在 JavaScript 中操作对象的唯一方法。
  • 正确。我的问题是,例如,为什么数字和字符串的行为方式不同,因为我误解了数字和字符串也是对象。如果我为变量 myNum 分配一个数字,那么 myNum.__proto__.__proto__.constructor === Object.然而 Object.getPrototypeOf(myNum) 引发了一个错误,抱怨 myNum 不是一个对象。 myNum 怎么可能不是一个对象,却有一个 .__proto__ 属性和一个终止于 Object 的原型链?

标签: javascript loops closures


【解决方案1】:

首先,“所有 JavaScript 类型都是对象”这句话是不正确的。原始字符串、数字和布尔值不是对象。

其次,JavaScript 中的 一切 都是按值传递的。了解“按值传递”的含义很重要。这意味着当一个函数被调用时,像这样:

var someVariable = something;
someFunction(someVariable); // <--- this is the function call

那么语言所做的是复制 someVariable 的值并将该副本传递给函数。 “按引用传递”语言所做的是将对该变量的引用传递给函数。因为变量值的副本是在传值世界中传递给函数的,所以函数绝对没有办法修改someVariable 的值。在“通过引用”的语言中,确实如此。

在某种程度上,C++ 允许您使用任一参数传递方案。 JavaScript 没有。

在 JavaScript 中变量具有对象引用作为值的事实有时并不意味着该语言是按引用传递的。我知道这似乎是一种愚蠢的迂腐区分,但重要的是要了解“按值传递”和“按引用传递”是用于描述语言语义的精确术语。如果它们没有确切的含义,那么它们对于那个目的是没有用的。


还有一件事:当原始值像对象一样使用时,JavaScript 会隐式地将字符串、数字和布尔原始值包装在相应的字符串、数字和布尔类型的包装器中。当您执行以下常见操作时会发生这种情况:

var five = "hello".length

. 运算符的左侧操作数必须是对象,因此这里没有特殊情况:原始字符串值隐式提升为 String 实例。 (运行时真正在幕后做了什么,好吧,我们不知道也不应该关心。从概念上讲,一个临时包装对象被创建、使用和丢弃。)

【讨论】:

  • 这个答案非常好。我只是不确定我问对了问题。也许我上面的后续问题会有所帮助:如果我将一个数字分配给一个名为 myNum 的变量,那么 myNum.__proto__.__proto__.constructor === Object.然而 Object.getPrototypeOf(myNum) 引发了一个错误,抱怨 myNum 不是一个对象。 myNum 怎么可能不是一个对象,却有一个 .__proto__ 属性和一个终止于 Object 的原型链?
  • @DanielArant 你被 JavaScript 和 . 操作符的行为误导了。如果您在. 前面放置一个非对象(如字符串或数字),该语言会自动将其包装在相应的对象包装实例(字符串或数字类型)中。那些东西对象,但原始值不是。
  • 啊,现在说得通了!您可以编辑您的答案以包含它,还是应该将我的后续问题作为一个单独的问题发布? (假设它还没有被问到?)
  • @DanielArant 我会编辑它;我应该首先包含该注释:)
【解决方案2】:

在 javascript 中有 6 种原语类型:字符串、数字、布尔值、null、未定义、符号 - ECMAScript 2015 中的新增内容。但是这些原语有 Object 包装类。除了 nullundefined 之外的所有 6 个都有包装类。 Reference 这些原始类型是按值传递的,而不是按 javascript 设计的引用传递。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 2014-09-24
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多