掌握 JavaScript 中的 按值”和 按引用”
掌握 JavaScript 中的“按值”和“按引用”
原文:https://medium.com/hackernoon/grasp-by-value-and-by-reference-in-javascript-7ed75efa1293
并了解为什么知道其中的区别是至关重要的

JavaScript 是一种面向对象的语言:这意味着 JavaScript 中的大多数东西都是对象。例如,函数就是对象。唯一不是对象的元素是原始数据类型: 字符串,数字,布尔,空值和未定义。这些原始数据类型也是不可变的,这意味着一旦创建就不能修改。
两者的区别之一是原始数据类型通过值传递,对象通过引用传递。
原始数据类型通过值传递,对象通过引用传递。**
这是什么意思?你可以这样想:
- 按值意味着创建原始的副本。把它想象成双胞胎:他们生来一模一样,但是第一个双胞胎没有失去一条腿,而第二个双胞胎在战争中失去了一条腿。
- 引用是指为原创建一个别名。当你妈妈叫你“南瓜派”的时候,虽然你的名字是玛格丽特,但这并不会突然产生一个克隆的你:你仍然是一个,但你可以被这两个截然不同的名字所称呼。
让我们看看原语和对象是如何表现的,首先当我们用赋值操作符(=)给它们赋值时,其次当我们将它们作为参数传递给函数时。
1.用=运算符为基本对象赋值
对于原始数据类型,=运算符按值工作
考虑以下代码:
var name = "Carlos";
var firstName = name;
name = "Carla";console.log(name); // "Carla"
console.log(firstName); // "Carlos"
→ 自己试试
结果非常简单:这就是=运算符按值工作。这里实际发生的事情可以简化如下:
- 变量
name被创建并被赋予值“Carlos”。JavaScript 为它分配一个内存点。 - 变量
firstName被创建并被赋予name值的副本。firstName有自己的内存点,独立于name。这时在代码中,firstName也有一个值“卡洛斯”。 - 然后我们将
name的值改为“Carla”。但是firstName仍然保持其原始值,因为它位于不同的内存点。
当处理原语时,运算符=创建原始变量的副本。这就是价值的含义。
对于对象,=运算符通过引用工作
考虑以下代码:
var myName = {
firstName: "Carlos"
};
var identity = myName;
myName.firstName = "Carla";console.log(myName.firstName); // "Carla"
console.log(identity.firstName); // "Carla"
→ 自己试试
这里,包含对象的变量的输出是相同的。发生这种情况是因为,当处理对象时,=操作符通过引用工作。实际发生的情况可以描述如下:
- 变量
myName被创建,并被赋予一个具有属性firstName的对象的值。firstName有“卡洛斯”的价值。JavaScript 为myName和它包含的对象分配一个内存点。 - 变量
identity被创建并指向myName。没有专用的内存空间给*identity*的值。它只指向的myName的值。 - 我们将
myName的firstName属性的值改为“Carla”而不是“Carlos”。
当我们记录myName.firstName时,它显示新的值,这非常简单。但是当我们记录identity.firstName时,它的也会显示myName.firstName的新值“Carla”。这是因为只有identity.firstName指向 myName.firstName在内存中的位置。
处理对象时,运算符=会创建原始对象的别名,而不会创建新对象。这就是“通过引用”的意思。
2.将原语和对象传递给函数
原始数据类型通过值传递给函数
如果在函数内部更改原始数据类型的值,这种更改不会影响外部作用域中的变量:
var myName = "Carlos";
function myNameIs(aName){
aName = "Carla";
}
myNameIs(myName);console.log(myName); // "Carlos"
→ 自己试试
即使我们在函数myNameIs内部改变myName变量,当我们在调用函数后打印它时,它仍然有值“Carlos”。这是因为当传递基本类型时,它们是通过值传递的。
我们传递的是myName的副本:你在函数体内对myName做的任何事情都不会影响全局范围内的myName变量,因为你传递的是myName的副本,而不是原始的myName变量。
对象通过引用传递给函数
当你通过引用传递东西时,你传递的是指向其他东西的东西,而不是对象的副本。因此,由于 JavaScript 通过引用传递对象,当您在函数中更改该对象的属性时,更改将反映在外部范围中:
var myName = {};
function myNameIs(aName){
aName.firstName = "Carla";
}
myNameIs(myName);console.log(myName); // Object {firstName: "Carla"}
→ 自己试试
现在,如果我在调用函数myNameIs后记录myName变量,它会记录一个键为firstName的对象,其值等于“Carla”。当我们将对象传递给函数时,它在全局范围内发生了变化。
这是因为当您将一个对象传递给函数时,您传递的不是一个副本。你正在传递一个将指向myName对象的东西。因此,当您在函数中更改该对象的属性时,您正在更改外部作用域中该对象的属性。
但是有一点你应该注意:
var myName = {
firstName: "Carla"
};function myNameIs(aName){
aName = {
nickName: "Carlita"
};
}myNameIs(myName);
console.log(myName); // Object {firstName: "Carla"}
→ 自己试试
这里它打印了外部作用域中变量myName的值,这次没有给对象添加一个nickName属性。这是为什么呢?如果你仔细观察,我们在函数中所做的是试图给myName对象重新赋值。
但是你不能把myName 指向的东西,你只能把myName的一个属性改成别的,就像这样:
var myName = {
firstName: "Carla"
};function myNameIs(aName){
aName.nickName = "Carlita";
}myNameIs(myName);
console.log(myName); // Object {firstName: "Carla", nickName: "Carlita"}
→ 自己试试
想了解更多?查看我关于 JavaScript 基础的其他文章:
- JavaScript 中的吊装:快速指南
- 轻松理解 JavaScript 变量作用域
- [如何自信地使用 JavaScript 闭包](https://hackernoon.com/how-to-use-javascript-closures-with-confidence-85cd1f841a6b)
- [JavaScript 中的日期快速手册](https://hackernoon.com/a-quick-handbook-for-dates-in-javascript-7b71d0ef8e53)
- [像老板一样使用 JavaScript 数组](/@lenafaure/work-with-javascript-arrays-like-a-boss-97207a042e42)
我希望你喜欢这个“按值”和“按引用”的解释。
请随意评论并喜欢这篇文章,以便其他人可以在 Medium 上轻松找到它!




