Menu Close

JavaScript 值传递与引用传递

本教程解释了 JavaScript 中按值传递的工作原理,并为你提供了一些将原始值和引用值传递给函数的示例。

在继续本教程之前,你应该对以下内容有较好的了解:JavaScript 原始值与引用值

JavaScript 是“按值传递”还是“按引用传递”?

JavaScript 中,所有函数参数始终都是按值传递的。这意味着 JavaScript 会将变量的值复制到函数的参数中。你在函数内部对参数所做的任何更改都不会影响函数外部传入的变量。换句话说,对参数所做的更改在函数外部不会反映出来。

如果函数参数是按引用传递的,那么你传入函数的变量的更改将在函数外部可见。但在 JavaScript 中,这是不可能的。

原始值的按值传递

JavaScript 中的原始值包括:

  • Number(数字)
  • String(字符串)
  • Boolean(布尔值)
  • Null
  • Undefined
  • Symbol

让我们来看下面这个例子。

function square(x) {
    x = x * x;
    return x;
}

let y = 10;
let result = square(y);

console.log(result); // 100 
console.log(y); // 10 -- no change

该脚本是如何工作的?

首先,定义一个名为 square() 的函数,该函数接收一个参数 x。该函数将 x 的平方赋值给参数 x

接着,声明变量 y 并将其初始化为 10

声明变量 y 并将其初始化为 10:然后,将变量 y 传递给 square() 函数。当将变量 y 传入 square() 函数时,JavaScript 会将 y 的值复制给变量 x

之后,square() 函数修改了变量 x。然而,这并不会影响变量 y 的值,因为 xy 是独立的变量。

最后,在 square() 函数执行完毕后,变量 y 的值没有发生变化。

如果 JavaScript 使用按引用传递,那么在调用函数之后,变量 y 的值将会变为 100。

当你将一个原始值从一个变量赋值给另一个变量时,该值会被复制到新变量中。这两个变量各自持有该值的独立副本。

再例如:

let a = 5;
let b = a;
b = 10;
console.log(a); // 5
console.log(b); // 10 尽管 b 被赋予了 a 的值,但修改 b 不会影响 a

引用值的按值传递

引用值也是按值传递的,这一点并不容易看出来。例如:

let person = {
  name: 'John',
  age: 25,
};

function increaseAge(obj) {
  obj.age += 1;
}

increaseAge(person);

console.log(person);

该脚本是如何工作的?

首先,定义变量 person,它引用一个包含两个属性 nameage 的对象:

接着,定义一个名为 increaseAge() 的函数,该函数接收一个对象参数 obj,并将该对象的 age 属性增加 1。

然后,将 person 对象传递给 increaseAge() 函数:

在内部,JavaScript 引擎会创建一个 obj 引用,并使该变量引用与 person 变量相同的对象。

之后,通过 obj 变量在 increaseAge() 函数内部将 age 属性增加 1。

最后,通过 person 引用访问该对象:

看起来 JavaScript 是通过引用传递对象的,因为对对象的修改在函数外部也能反映出来。然而,事实并非如此。

实际上,当你将一个对象传递给函数时,传递的是该对象的引用,而不是对象本身。因此,函数可以通过这个引用来修改对象的属性。

然而,你无法在函数内部更改传入的引用。例如:

let person = {
  name: 'John',
  age: 25,
};

function increaseAge(obj) {
  obj.age += 1;

  // reference another object
  obj = { name: 'Jane', age: 22 };
}

increaseAge(person);

console.log(person);

输出结果:

{ name: 'John', age: 26 }

在这个例子中,increaseAge() 函数通过参数 obj 修改了 age 属性:

并让 obj 引用另一个对象:

然而,person 引用仍然指向原始对象,该对象的 age 属性已经被修改为 26。换句话说,increaseAge() 函数并没有改变 person 的引用。

如果这个概念仍然让你感到困惑,你可以将函数参数看作是局部变量。

如何复制对象?

如果你想创建一个对象的副本,而不是共享同一个引用,你需要显式地复制对象。

一种常见的方法是使用 Object.assign() 方法或扩展运算符...)来创建浅拷贝。


Example using Object.assign():
使用 Object.assign() 的示例:

let original = { a: 1, b: 2 };
let copy = Object.assign({}, original);
copy.a = 99;
console.log(original.a); // 1
console.log(copy.a);     // 99

使用扩展运算符的示例:

let original = { a: 1, b: 2 };
let copy = { ...original };
copy.b = 42;
console.log(original.b); // 2
console.log(copy.b);     // 42

注意,这些都是浅拷贝,所以如果对象中有嵌套对象,嵌套对象依然是通过引用共享的。

结论

理解 JavaScript 如何处理值传递和引用传递对避免错误和编写可预测的代码非常重要

  • 原始类型是按值传递的。
  • 对象是按引用传递的。
  • 要复制对象而不共享引用,可以使用 Object.assign() 或扩展语法。
READ  JavaScript 函数 Functions, 变量,常量,全局变量和局部变量

 

 

除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!
Posted in Javascript 函数

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Leave the field below empty!