一文详解JavaScript中的按值传递和按引用传递

 更新时间:2024年05月07日 11:01:51   作者:刘同学有点忙  
编程语言中,把一个变量的值赋值给另一个变量,或者给函数调用传递参数有两种方式:按值传递和按引用传递,本文将给大家详细介绍JavaScript中的按值传递和按引用传递,需要的朋友可以参考下

JavaScript中几乎都是按值传递

编程语言中,把一个变量的值赋值给另一个变量,或者给函数调用传递参数有两种方式:按值传递按引用传递

JavaScript中几乎都是按值传递。我们看一个例子:

let a = 1
let b = a
b = 2
console.log(a) // 1
  • 上面的代码,声明了一个变量a,赋值为1;
  • 然后又声明了一个变量b,将变量a的值1赋值给变量b,此时变量b的值也是1;
  • 接着我们将变量b的值修改为2;
  • 此时打印变量a的值应该仍然是1,而不是2。

这就是按值传递,我们把变量a的值赋值给变量b的时候,只是把1这个值复制了一份给变量b,变量b的值的修改并不会影响到变量a的值。

很好,到目前为止,我们说上面的代码是按值传递很好理解,很符合我们的直觉。

上面是基元值的情况,如果换成引用类型的值呢?看下面的代码。

const foo = {
    a: 1
}
const bar = foo
bar.a = 2
console.log(foo.a) // 2
  • 上面的代码声明了一个变量foo,给它赋值了一个对象;
  • 然后又声明了一个变量bar,把变量foo指向的对象赋值给变量bar
  • 接着我们通过bar.a把对象的属性a修改为2;
  • 我们发现foo.a也被修改了!

说好的按值传递呢?如果是按值传递,修改bar.a不应该导致foo.a被修改啊,这好像不太符合直觉啊?难道引用类型的值是按引用传递吗?

并不是。JavaScript中引用类型的值也是按值传递的,只不过这个传递的值是对象在堆内存中的地址。

看上面的图片可以更清楚地理解这个过程,假设对象在堆内存中的地址是0x100,那么按值传递的就是0x100这个地址。bar.a修改了对象里属性的值,但是foobar仍都然都指向地址0x100,所以通过bar.a修改对象属性值会反应到foo.a上。

上面的图只是一个粗略的方便理解的图,下面的图可能更符合代码实际的内存分布。

另外,仍然是上面的代码,如果我们稍加改动,给变量bar赋值一个新的对象,那么变量foo和变量bar就指向不同的内存地址了,修改变量bar将不再导致变量foo被修改。有些支持按引用传递的语言,类似的操作会导致变量foo也被修改,这个我不太了解,所以就不展开了。

const foo = {
    a: 1
}
let bar = foo
bar = {
    a: 2
}
console.log(foo.a) // 1

ES Module中的live bindings

前面我们说了,JavaScript中几乎都是按值传递,这样说通常都有例外。ES Module中export导出的变量被称为live bindings(实时绑定),这是JavaScript中唯一按引用传递的情况。

// a.js
export let count = 1
export function increment() {
    count++
}
// b.js
import { count, increment } from './a.js'
console.log(count) // 1
// count = 2 // import的变量是只读的,不能修改,尝试修改会报错 TypeError: Assignment to constant variable.
increment()
console.log(count) // ?

让我们暂停下来,思考一下,上面的代码中,第二个console.log(count)会输出什么?

答案是2。ES Module中export的变量,其它模块import进来之后是只读的,尝试修改会报错。但是export变量的模块可以另外导出一个方法用来修改这个变量,变量的修改会同步反应在两个模块中,这种情况被称为live bindings,是按引用传递的。

上面类似的代码在CommonJs中的执行结果截然不同。

// a.js
let count = 1
function increment() {
    count++
}
module.exports = {
    count,
    increment
}
const { count, increment } = require('./a.js')
console.log(count) // 1
count = 2  // 可以修改
console.log(2) // 2
increment()
console.log(count) // 是2而不是3

require导入的变量是可以被修改的,上面的代码中最后的console.log(count)的值是2而不是3,因为这里count是按值传递的。

总结

  • JavaScript中几乎都是按值传递。
  • ES Module中export导出的变量是JavaScript中唯一的按引用传递,这被称作live bindings。另外export导出的变量是只读的,在模块外部不允许修改它的值,通常可以额外导出一个方法用来修改这个变量。

以上就是一文详解JavaScript中的按值传递和按引用传递的详细内容,更多关于JavaScript按值和按引用传递的资料请关注脚本之家其它相关文章!

相关文章

  • javascript学习笔记之函数定义

    javascript学习笔记之函数定义

    本文主要给大家介绍了javascript的一些函数定义方面的基础知识,包括函数声明式、函数表达式、Function 构造函数等,十分的简单实用,有需要的小伙伴可以参考下。
    2015-06-06
  • Bootstrap 实现查询的完美方法

    Bootstrap 实现查询的完美方法

    Bootstrap,来自 Twitter,是目前最受欢迎的前端框架。这篇文章主要介绍了Bootstrap 实现查询的完美方法,需要的朋友可以参考下
    2016-10-10
  • js借助ActiveXObject实现创建文件

    js借助ActiveXObject实现创建文件

    创建文件的方法有很多,在本文为大家详细介绍下js中时如何实现的,感兴趣的朋友不要错过了
    2013-09-09
  • 原生javascript+CSS实现轮播图效果

    原生javascript+CSS实现轮播图效果

    这篇文章主要为大家详细介绍了原生javascript+CSS实现轮播图效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • JavaScript注册监听事件和清除监听事件方式详解

    JavaScript注册监听事件和清除监听事件方式详解

    js中事件监听就是利用addEventListener来绑定一个事件,这个用法在jquery中非常常用并且简单,但在原生js中比较复杂,下面这篇文章主要给大家介绍了关于JavaScript注册监听事件和清除监听事件方式的相关资料,需要的朋友可以参考下
    2023-05-05
  • 17道题让你彻底理解JS中的类型转换

    17道题让你彻底理解JS中的类型转换

    这篇文章主要给大家介绍了关于如何通过17道题让你彻底理解JS中的类型转换的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用JS具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • 使用BroadcastChannel进行跨窗口通信的实例详解

    使用BroadcastChannel进行跨窗口通信的实例详解

    BroadcastChannel 提供了一种简单而有效的方式来实现同一浏览器环境下不同页面或脚本之间的通信,对于需要跨窗口、标签页或 iframe 同步数据的应用场景,它是一种非常便捷的解决方案,本文介绍了如何使用 BroadcastChannel 进行跨窗口通信,需要的朋友可以参考下
    2024-08-08
  • JS定义类的六种方式详解

    JS定义类的六种方式详解

    下面小编就为大家带来一篇JS定义类的六种方式详解。小编觉得挺不错的,现在分享给大家,也给大家做个参考,一起跟随小编过来看看吧,祝大家游戏愉快哦
    2016-05-05
  • JS中比Switch...Case更优雅的多条件判断写法

    JS中比Switch...Case更优雅的多条件判断写法

    这篇文章主要给大家介绍了关于JS中比Switch...Case更优雅的多条件判断写法,文中通过示例代码介绍的非常详细,对大家学习或者使用JS具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • Javascript实现html转pdf高清版(提高分辨率)

    Javascript实现html转pdf高清版(提高分辨率)

    这篇文章主要介绍了Javascript将html转成pdf高清版(提高分辨率),需要的朋友可以参考下
    2020-02-02

最新评论