JavaScript变量复制值和传递参数

简介: 相比于其他语言,JavaScript 中的变量可谓独树一帜。正如 ECMA-262 所规定的,**JavaScript 变量是松散类型的**,而且变量不过就是特定时间点一个特定值的名称而已。由于没有规则定义变量必须包含什么数据类型,变量的值和数据类型在脚本生命期内可以改变。这样的变量很有意思,很强大,当然也有不少问题。本章会剖析错综复杂的变量。

👦个人简介:张清悠,字澄澈,号寻梦客,爱好旅行、运动,主攻前端方向技术研发,副攻Unity 3D、C++、Python人工智能等
📝个人寄语:学会不为过程的缓慢而焦虑,即使暂时未能如你所愿,但只要你在努力,你就在不断成长!
🙏个人公众号:清悠小猿(海量源码尽在其中,欢迎关注)

前言:

相比于其他语言,JavaScript 中的变量可谓独树一帜。正如 ECMA-262 所规定的,JavaScript 变量是
松散类型的
,而且变量不过就是特定时间点一个特定值的名称而已。由于没有规则定义变量必须包含什
么数据类型,变量的值和数据类型在脚本生命期内可以改变。这样的变量很有意思,很强大,当然也有
不少问题。本章会剖析错综复杂的变量。

一、复制值

除了存储方式不同,原始值和引用值在通过变量复制时也有所不同。在通过变量把一个原始值赋值
到另一个变量时,原始值会被复制到新变量的位置。请看下面的例子:

let num1 = 5; 
let num2 = num1;

这里,num1 包含数值 5。当把 num2 初始化为 num1 时,num2 也会得到数值 5。这个值跟存储在
num1 中的 5 是完全独立的,因为它是那个值的副本。
这两个变量可以独立使用,互不干扰。这个过程如图 所示

7b699d9ed0358f13fa4812c0002e35f.png
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区
别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。操作完成后,两个变量实际
上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来,如下面的例子所示:

let obj1 = new Object(); 
let obj2 = obj1; 
obj1.name = "Nicholas"; 
console.log(obj2.name); // "Nicholas"

在这个例子中,变量 obj1 保存了一个新对象的实例。然后,这个值被复制到 obj2,此时两个变
量都指向了同一个对象。在给 obj1 创建属性 name 并赋值后,通过 obj2 也可以访问这个属性,因为
它们都指向同一个对象。如图展示了变量与堆内存中对象之间的关系

e5bcb1a3b88668cfc460c2d960f8a3e.png

二、传递参数

ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数
中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是
引用值,那么就跟引用值变量的复制一样。对很多开发者来说,这一块可能会不好理解,毕竟变量有按
值和按引用访问,而传参则只有按值传递。
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用 ECMAScript 的话说,
就是 arguments 对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变
量,这意味着对本地变量的修改会反映到函数外部。(这在 ECMAScript 中是不可能的。)来看下面这个
例子:

function addTen(num) {
   
    
 num += 10; 
 return num; 
} 
let count = 20; 
let result = addTen(count); 
console.log(count); // 20,没有变化
console.log(result); // 30

这里,函数 addTen()有一个参数 num,它其实是一个局部变量。在调用时,变量 count 作为参数
传入。count 的值是 20,这个值被复制到参数 num 以便在 addTen()内部使用。在函数内部,参数 num
的值被加上了 10,但这不会影响函数外部的原始变量 count。参数 num 和变量 count 互不干扰,它们
只不过碰巧保存了一样的值。如果 num 是按引用传递的,那么 count 的值也会被修改为 30。这个事实
在使用数值这样的原始值时是非常明显的。但是,如果变量中传递的是对象,就没那么清楚了。比如,
再看这个例子:

function setName(obj) {
   
    
 obj.name = "Nicholas"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas"

这一次,我们创建了一个对象并把它保存在变量 person 中。然后,这个对象被传给 setName()
方法,并被复制到参数 obj 中。在函数内部,obj 和 person 都指向同一个对象。结果就是,即使对象
是按值传进函数的,obj 也会通过引用访问对象。当函数内部给 obj 设置了 name 属性时,函数外部的
对象也会反映这个变化,因为 obj 指向的对象保存在全局作用域的堆内存上。很多开发者错误地认为,
当在局部作用域中修改对象而变化反映到全局时,就意味着参数是按引用传递的。为证明对象是按值传
递的,我们再来看看下面这个修改后的例子:

function setName(obj) {
   
    
 obj.name = "Nicholas"; 
 obj = new Object(); 
 obj.name = "Greg"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas"

这个例子前后唯一的变化就是 setName()中多了两行代码,将 obj 重新定义为一个有着不同 name
的新对象。当 person 传入 setName()时,其 name 属性被设置为"Nicholas"。然后变量 obj 被设置
为一个新对象且 name 属性被设置为"Greg"。如果 person 是按引用传递的,那么 person 应该自动将
指针改为指向 name 为"Greg"的对象。可是,当我们再次访问 person.name 时,它的值是"Nicholas",
这表明函数中参数的值改变之后,原始的引用仍然没变。当 obj 在函数内部被重写时,它变成了一个指
向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了

总结:

```
本期我们分享的是JavaScript变量(二)复制值和传递参数,
我们下期:分享复确定类型以及执行上下文与作用域
原创不易,期待您的点赞关注与转发评论😜😜

目录
相关文章
|
25天前
|
JavaScript 前端开发
JavaScript基础知识-变量的声明提前
关于JavaScript变量声明提前特性的基础知识介绍。
30 0
JavaScript基础知识-变量的声明提前
|
3月前
|
JavaScript 前端开发 开发者
JavaScript的变量提升是一种编译阶段的行为,它将`var`声明的变量和函数声明移至作用域顶部。
【6月更文挑战第27天】JavaScript的变量提升是一种编译阶段的行为,它将`var`声明的变量和函数声明移至作用域顶部。变量默认值为`undefined`,函数则整体提升。`let`和`const`不在提升范围内,存在暂时性死区。现代实践推荐明确声明位置以减少误解。
36 2
|
2天前
|
存储 前端开发 JavaScript
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
本文介绍了JavaScript变量的声明和使用、标识符的命名规则、如何获取和操作HTML元素,以及JavaScript的鼠标事件处理,通过示例代码展示了这些基础知识点在实际开发中的应用。
13 2
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
|
2天前
|
JavaScript 前端开发
js 变量作用域与解构赋值| 22
js 变量作用域与解构赋值| 22
|
27天前
|
存储 JavaScript 前端开发
|
1月前
|
JavaScript 前端开发
揭秘JavaScript变量的三大守护神:从var到let,再到const,究竟隐藏了哪些秘密?
【8月更文挑战第22天】在JavaScript中,`var`、`let`和`const`用于声明变量,但各有特点。`var`有函数作用域并会被提升至作用域顶部。`let`提供块级作用域且存在暂时性死区,不允许提前访问。`const`同样拥有块级作用域,用于声明常量,一旦初始化便不可改变。现代开发倾向于使用`let`和`const`以获得更清晰的作用域控制和避免潜在错误。
27 0
|
1月前
|
JavaScript 前端开发
揭开JavaScript变量作用域与链的神秘面纱:你的代码为何出错?数据类型转换背后的惊人秘密!
【8月更文挑战第22天】JavaScript是Web开发的核心,了解其变量作用域、作用域链及数据类型转换至关重要。作用域定义变量的可见性与生命周期,分为全局与局部;作用域链确保变量按链式顺序查找;数据类型包括原始与对象类型,可通过显式或隐式方式进行转换。这些概念直接影响代码结构与程序运行效果。通过具体示例,如变量访问示例、闭包实现计数器功能、以及动态表单验证的应用,我们能更好地掌握这些关键概念及其实践意义。
24 0
|
2月前
|
设计模式 JavaScript 前端开发
JS 代码变量和函数的正确写法
JS 代码变量和函数的正确写法
41 3
|
1月前
|
JavaScript 前端开发
JavaScript声明变量的几种方式
JavaScript声明变量的几种方式
24 0
|
2月前
|
JavaScript
js export 对外输出常量、变量和函数
js export 对外输出常量、变量和函数
68 5