注册
web

const声明的变量还能修改?原理都在这了

前言


const关键字用于声明只读的常量,一旦被赋值,其值就不能再被改变。但是,这并不意味着该变量持有的值是不可变的。


本文将结合案例逐步探讨JavaScript中const声明的工作原理,解释为何这些看似常量的变量却可以被修改,并探讨其实际应用。


耐心看完,你一定有所收获。


giphy.gif


正文


const关键字用于声明一个变量,该变量的值在其生命周期中不会被重新赋值。


现象


1. 基本数据类型


对于基本数据类型(如数字、字符串、布尔值),const确保变量的值不会改变。


const num = 42;
// num = 43; // 这会抛出错误

2. 对象


对于对象,仍然可以修改对象的属性,但不能重新赋值整个对象。


const girlfriend = {
name: "小宝贝"
};

girlfriend.name = "亲爱的"; // 这是允许的,因为你只是修改了对象的一个属性

// girlfriend = { name: "亲爱的" }; // 这会抛出错误,因为你试图改变obj的引用

假如你有个女朋友,也许并没有,但我们可以假设,她的名字或者你平时叫她的昵称是"小宝贝"。


有一天,你心血来潮,想换个方式叫她,于是叫她"亲爱的"。这完全没问题,因为你只是给她换了个昵称,她本人并没有变。


但是,如果有一天你看到另一个女生,你却说:“哎,这不是亲爱的吗?”这就出大问题了!因为你把一个完全不同的人当成了你的女朋友。


这就像你试图改变girlfriend的引用,把它指向了一个新的对象。


JavaScript不允许这样做,因为你之前已经明确地告诉它,girlfriend就是那个你叫"小宝贝"的女朋友,你不能突然把另一个人说成她。


154eb98c10eaf8356b5da0e44b9e9fe6.gif


简单来说,你可以随时给你的女朋友起个新昵称,但你不能随便把别的女生当成你的女朋友。


3. 数组


对于数组,你可以修改、添加或删除元素,但不能重新赋值整个数组。


const arr = [1, 2, 3];

arr[0] = 4; // 这是允许的,因为你只是修改了数组的一个元素
arr.push(5); // 这也是允许的,因为你只是向数组添加了一个元素

// arr = [6, 7, 8]; // 这会抛出一个错误,因为你试图改变arr的引用

假设arr是你的超市购物袋,里面有三个苹果,分别标记为1、2和3。


你检查了第一个苹果,觉得它不够新鲜,所以你把它替换成了一个新的苹果,标记为4。这就像你修改数组的一个元素。这完全可以,因为你只是替换了袋子里的一个苹果。


后来,你决定再放一个苹果进去,标记为5。这也没问题,因为你只是向袋子里添加了一个苹果。


苹果再变,袋子仍然是原来的袋子。


但是,当你试图拿个新的装着6、7、8的购物袋来代替你原来的袋子时,就不对了。你不能拿了一袋子苹果,又扔在那不管,反而又去拿了一袋新的苹果。


你礼貌吗?


f2e0de05371993107839d315b5639a30.jpg


你可以随时替换袋里的苹果或者放更多的苹果进去,但你不能拿了一袋不要了又拿一袋。


原理


在JavaScript中,const并不是让变量的值变得不可变,而是让变量指向的内存地址不可变。换句话说,使用const声明的变量不能被重新赋值,但是其所指向的内存中的数据是可以被修改的。


使用const后,实际上是确保该变量的引用地址不变,而不是其内容。


结合上面两个案例,女朋友和购物袋就好比是内存地址,女朋友的外号可以改,但女朋友是换不了的,同理袋里装的东西可以换,但袋子仍然是那个袋子。


当使用const声明一个变量并赋值为一个对象或数组,这个变量实际上存储的是这个对象或数组在内存中的地址,形如0x00ABCDEF(这只是一个示例地址,实际地址会有所不同),而不是它的内容。这就是为什么我们说变量“引用”了这个对象或数组。


实际应用


这种看似矛盾的特性实际上在开发中经常用到。


例如,在开发过程中,可能希望保持一个对象的引用不变,同时允许修改对象的属性。这可以通过使用const来实现。


考虑以下示例:


假设你正在开发一个应用,该应用允许用户自定义一些配置设置。当用户首次登录时,你可能会为他们提供一组默认的配置。但随着时间的推移,用户可能会更改某些配置。


// 默认配置
const userSettings = {
theme: "light", // 主题颜色
notifications: true, // 是否开启通知
language: "en" // 默认语言
};

// 在某个时间点,用户决定更改主题颜色和语言
function updateUserSettings(newTheme, newLanguage) {
userSettings.theme = newTheme;
userSettings.language = newLanguage;
}

// 用户调用函数,将主题更改为"dark",语言更改为"zh"
updateUserSettings("dark", "zh");

console.log(userSettings); // 输出:{ theme: "dark", notifications: true, language: "zh" }

在这个例子中,我们首先定义了一个userSettings对象,它包含了用户的默认配置。尽管我们使用const来声明这个对象,但我们仍然可以随后更改其属性来反映用户的新配置。


这种模式在实际开发中很有用,因为它允许我们确保userSettings始终指向同一个对象(即我们不会意外地将其指向另一个对象),同时还能够灵活地更新该对象的内容以反映用户的选择。


为什么不用let


以上所以案例中,使用let都是可行,但它的语义和用途相对不同,主要从这几个方面进行考虑:



  1. 不变性:使用const声明的变量意味着你不打算重新为该变量赋值。这为其他开发人员提供了一个明确的信号,即该变量的引用不会改变。在上述例子中,我们不打算将userSettings重新赋值为另一个对象,我们只是修改其属性。因此,使用const可以更好地传达这一意图。
  2. 错误预防:使用const可以防止意外地重新赋值给变量。如果你试图为const变量重新赋值,JavaScript会抛出错误。这可以帮助捕获潜在的错误,特别是在大型项目或团队合作中。
  3. 代码清晰度:对于那些只读取和修改对象属性而不重新赋值的场景,使用const可以提高代码的清晰度,可以提醒看到这段代码的人:“这个变量的引用是不变的,但其内容可能会变。”

一般我们默认使用const,除非确定需要重新赋值,这时再考虑使用let。这种方法旨在鼓励不变性,并使代码更加可预测和易于维护。


避免修改


如果我们想要避免修改const声明的变量,当然也是可以的。


例如,我们可以使用浅拷贝来创建一个具有相同内容的新对象或数组,从而避免直接修改原始对象或数组。这可以通过以下方式实现:


const originalArray = [1, 2, 3];
const newArray = [...originalArray]; // 创建一个原始数组的浅拷贝
newArray.push(4); // 不会影响原始数组
console.log(originalArray); // 输出: [1, 2, 3]
console.log(newArray); // 输出: [1, 2, 3, 4]

总结


const声明的变量之所以看似可以被修改,是因为const限制的是变量指向的内存地址的改变,而不是内存中数据的改变。这种特性在实际开发中有其应用场景,允许我们保持引用不变,同时修改数据内容。


然而,如果我们确实需要避免修改数据内容,可以采取适当的措施,如浅拷贝。


9a9f1473841eca9a3e5d7e1408145a4b.gif

0 个评论

要回复文章请先登录注册