一文详解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
修改了对象里属性的值,但是foo
和bar
仍都然都指向地址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按值和按引用传递的资料请关注脚本之家其它相关文章!
相关文章
使用BroadcastChannel进行跨窗口通信的实例详解
BroadcastChannel 提供了一种简单而有效的方式来实现同一浏览器环境下不同页面或脚本之间的通信,对于需要跨窗口、标签页或 iframe 同步数据的应用场景,它是一种非常便捷的解决方案,本文介绍了如何使用 BroadcastChannel 进行跨窗口通信,需要的朋友可以参考下2024-08-08Javascript实现html转pdf高清版(提高分辨率)
这篇文章主要介绍了Javascript将html转成pdf高清版(提高分辨率),需要的朋友可以参考下2020-02-02
最新评论