理解 Javascript 不可变变量

IT技术 javascript
2021-01-27 14:43:31

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

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

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

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

6个回答

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

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

以下值是原始

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

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

所以:

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

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

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

希望这可以帮助!

@pulp_fiction:是的,只要没有其他'var''references'那个'value'。但是,我想指出,在现实生活中,现代浏览器会进行各种优化,例如并非所有数字实际上都是浮动的,小整数是超快的(暗示它们内部也不是 32 位),观察并重新编译部分代码(取决于实际使用情况)等。 额外注意:如果您有一个数组缓冲区,则可以覆盖该缓冲区内的特定地址,使它们更可预测(就 CPU 上的实际内存负载而言)运行脚本)。
2021-03-22 14:43:31
谢谢它有帮助。但我不知道什么是唯一标识。另外,我能找到更多关于原语如何与这些包装器相关联的分析信息吗?对我来说似乎很奇怪,我可以在基元上调用一个方法(例如 aString.length)。
2021-03-25 14:43:31
因此,如果我理解正确,那么与 C 相比,在 JavaScript 中,如果您有一个引用的内存位置,例如 var x = 1; 具有内容 0000000000000001 那么“不变性”意味着我们不能将内存涂层的内容更改为 0000000000000011。如果是这种情况,那么如果我写 var x=1; x=2;每次 JavaScript 都会为我提供一个新的内存位置并垃圾收集旧的无人值守位置?
2021-03-26 14:43:31
@ChrisAnderson:引用a(在您的示例中)指向一个对象(a如果您愿意,也就是对象的“值” )。你根本 冻结的对象,因此,你可以添加/删除/修改对象的属性/方法(“内容”如果你愿意),甚至它的原型!因此,您的示例 ( const a = {}; a.b = 1; a.b === 1)完全按照它应该做的但是,如果您发布了const a = {}; a = 42;,那么应该抛出Uncaught SyntaxError
2021-03-28 14:43:31
包装具有的方法length请注意我的答案第二行中的大小写。这个链接:JavaScript Primitives 的秘密生活可能会有很大帮助!
2021-03-29 14:43:31

首先,在 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

string 的初始化要求一个缓冲区以适合“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

“字符串上的所有操作都是按值而不是按引用完成的”我很困惑。字符串真的不是对缓冲区的引用吗?
2021-03-31 14:43:31
@LeCurious 是的,您可以说在 javascript 中我们引用了只读缓冲区,而在 C 中我们引用了读写缓冲区。希望你有笑话:)
2021-04-10 14:43:31
@LeCurious 在 C 中,如果你喜欢这个 char str[] = "foo"。然后 str 将是缓冲区的引用(指针)。您可以使用此参考对该缓冲区进行更改。但是,在 JavaScript 中,如果你这样做 var str = "hi"。那么 str 是一个只读标识符,用于访问存储 hi 的缓冲区。
2021-04-11 14:43:31

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

我想我开始明白了,谢谢。你知道第一个值会发生什么(在我的例子中是“astring”)?它存在于内存中直到垃圾收集器通过?
2021-03-25 14:43:31

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

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!

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

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

值得注意的是,最后一个示例使用name而不是var newName,但它的工作方式与分配新缓冲区的方式相同。旧缓冲区“Don...”一直存在,直到垃圾收集处理它。
2021-04-07 14:43:31

在这里先了解一下,

let firstString = "Tap";

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

firstString[0] = "N";

console.log(firstString)   //Output: Tap

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