41

我试图了解 Javascript 不可变变量的含义。如果我能做到:

var x = "astring";
x = "str";
console.log(x); //logs str` , then why it is immutable?

我能想到的唯一答案(从 CI 的一点点知道)是 var x 是指向值为“astring”的内存块的指针,在第二条语句之后,它指向另一个值为“str”的块。是这样吗?

还有一个额外的问题:我对 Javascript 的值类型感到困惑。引擎盖下的所有变量都是对象吗?偶数和字符串?

4

6 回答 6

58

是不可变的;变量不是;它们持有对其(原始)值的引用。

三种基本类型string、number 和 boolean具有对应的类型,其实例是对象:String、Number、Boolean
它们有时被称为包装类型

以下值是原始的:

  • 字符串:“你好”
  • 数字:6、3.14(JavaScript 中的所有数字都是浮点数)
  • 布尔值:真、假
  • null:通常显式分配
  • undefined:通常是默认(自动分配的)值

所有其他值都是对象,包括原语的包装器。

所以:

  • 对象默认是可变的
  • 对象具有唯一的身份并通过引用进行比较
  • 变量保存对对象的引用
  • 原语是不可变的
  • 基元是按值比较的,它们没有个体身份

您可能会发现The Secret Life of JavaScript Primitives是一个很好的解释。

此外,在 ES6 中有一个新的const关键字,它创建一个只读的命名常量,它不能通过赋值更改值或在脚本运行时重新声明。

希望这可以帮助!

于 2013-04-20T01:20:12.147 回答
20

首先,在 C 中,“字符串是最后一个 elem = '\0' 的字符数组”。它们是可变的。
如果您在 C 中声明并初始化一个字符串,如下所示:

char str[] = "Foo";

您基本上在做的是保留 4 个字节(可能是 8 位字节,如果它伤害了您,请不要介意)。单词 str 用作指向该数组的第一个元素的指针。所以,如果你这样做:

str[0] or *(str) = 'G'

然后它将改变该地址的值,而不是创建新数组。您可以通过打印 str 的地址来验证它。在这两种情况下都是一样的。

现在如果 JavaScript 字符串是原始类型。对字符串的所有操作都是按值而不是按引用完成的。所以,这样做会产生真实的。

var str1 = "foo";
var str2 = "foo";
str1 === str2; => true

字符串的初始化要求缓冲区适合“foo”并将名称 str1 绑定到它。使它们不可变的原因是您无法更改该缓冲区。所以,你不能这样做:

str1[0] = 'G'

在非严格模式下执行此命令不会产生警告或错误,但不会更改 str1。您可以通过以下方式验证它

console.log(str1) => "foo"

但如果你这样做:

str1 = "goo"

你实际上在做的是你要求一个新的缓冲区来适应“goo”并将标识符 str1 绑定到它。包含“foo”的旧缓冲区没有变化。

那么,“foo”会发生什么?

Java Script 有一个自动垃圾收集器。当它看到一些内存块不再可以被任何标识符引用或......然后它认为该内存是空闲的。

数字,布尔值也是如此。现在,关于包装对象!每当您尝试像这样访问字符串上的属性时:

str1.length;

JavaScript 的作用是使用 String 类创建一个新对象并调用字符串上的方法。一旦函数调用返回,对象就会被销毁。下面的代码进一步解释了它:

var str = "nature"; 
str.does = "nurtures"; //defining a new property; 
console.log(str.does) => undefined

因为对象已被销毁。试试这个!

var str = new String("Nature");
str.does = "nurtures";
console.log(str) =>  ??

这个 str 真的是一个对象......

结论:在 C 中,在单个范围内,变量名用作指针。所以,int、float、string 都是可变的。但在 Java Script 中,原始类型变量名用作值而不是引用

参考:C++ 入门加,Java Script The Definitive Guide,C by Stephen Kochan

于 2013-07-11T15:21:04.557 回答
5

你是对的。字符串(和数字)在 java 脚本(和许多其他语言)中是不可变的。变量是对它们的引用。当您“更改变量的值”时,您正在更改变量引用的字符串(或其他),而不是值本身。

于 2013-04-20T01:20:22.633 回答
5

我认为许多新程序员认为不变性意味着原始值不能通过重新分配来改变。

var str = "testing";
var str = "testing,testing";
console.log(str); // testing, testing

var fruits = ["apple", "banana", "orange"];

fruits[0] = "mango";

console.log(fruits); //["mango", "banana", "orange"]

与可变和不可变类型相关的值可以通过重新分配来更改,如上面的字符串和数组示例所示。但是,这些数据类型具有关联的函数(方法),用于操作属于每种数据类型的值。这是可以看到可变性/不变性的地方。由于数组是可变的,因此数组方法的任何操作都会直接影响数组。例如,

var fruits = ["mango","banana", "orange"];
fruits.pop(); 
console.log(fruits) //["mango", "banana"]


The array.pop() method deleted "orange" from the original fruits array.
But with strings for example,


var name = "Donald Trump";
name.replace("Donald", "President");
console.log(name)//Donald Trump

the original string remains intact!

不变性不允许通过 string 方法对原始字符串进行任何更改。相反,如果将方法操作分配给如下变量,则该方法会生成一个新字符串:

var name = "Donald Trump";
var newName = name.replace("Donald", "President");
console.log(newName);//President Trump

于 2018-01-30T14:15:01.810 回答
1

我们先在这里了解一下,

let firstString = "Tap";

console.log(firstString);  //Output: Tap 

firstString[0] = "N";

console.log(firstString)   //Output: Tap

这是我们可以看到不可变效果的地方!

于 2021-06-14T14:30:08.250 回答
0

这个定义中的不变性是历史性的。它与其他编程语言可以做的事情有关。

我认为这是首先要理解的。对于只使用过 JavaScript 的程序员来说,这个问题可能看起来荒谬或不必要的迂腐。将基元描述为不可变就像将冰淇淋描述为无法跳跃一样。为什么我会认为它可以?只有与其他历史上的编程语言相关,在处理原始类型时才明显缺乏可变性。

于 2021-11-09T22:52:27.603 回答