龙空技术网

晨叔技术晨报:你真的搞懂JS中的“值传递”和“引用传递”吗?

20k纯帅 98

前言:

如今咱们对“js二值化”大概比较关心,你们都需要剖析一些“js二值化”的相关资讯。那么小编也在网摘上汇集了一些有关“js二值化””的相关文章,希望同学们能喜欢,小伙伴们快快来了解一下吧!

晨叔周刊,每周一话题,技术天天涨。

温馨提示:今天补发本周二的内容, 下一篇于1月18号(后天)发布,敬请期待。



或者可以直接识别下图的话题海报进入本周的话题。

今天的技术晨报,就来谈谈JS中变量的,值传递和引用传递的问题。

现在,对于很多的JSer来讲,基本不关心堆和栈的问题,代码照样666。

但是,现在的前端,不再是传统的JQ时代,而是MVVM,组件化,工程化。

前端的承载着复杂业务逻辑。

为此,内存问题,成为JSer必须要考虑的问题。

本文从堆栈讲起,让大家理解JS中变量的内存使用以及变动情况

一、初步了解堆栈

先初步了解JS中的堆和栈。

首先,内存空间分为 堆和栈两个区域。

js 代码运行时,js解析器会先判断变量类型,根据变量类型,将变量放到不同的内存空间中(堆和栈)。

图1

基本的数据类型

(String,Number,Boolean,Null,Undefined)都会分配栈区。

而Object (对象)类型的变量都放到堆区。


如下代码示例

 var a = 12; var b = false; var c  = "string" var chenshu =  {name:"晨叔周刊",desc:"每周一话题,技术天天涨" }

对应的内存分配图如下图2

图2

栈区的特点:空间小,数据类型简单,读写速度快,一般由JS引擎自动释放


堆区的特点:空间大,数据类型复杂,读写速度稍逊,当对象不在被引用时,才会被周期性的回收。


二、变量传递

进入今天的重点,先看看下面的代码。


var a = 12;var b= a; b = 13;

上面代码的运行结果,a 变量的值没变,

因为 第二行“b = a” ,把a的值赋值个b时,

执行的是“复制”的操作,a 和 b 没有关系。

(so easy ,不在多BB),再往下看


var chenshu =  {name:"晨叔周刊",desc:"每周一话题,技术天天涨" };var xiaoming = chenshu;

chenshu 不是基础类型变量, 而是一个对象。

第二行中,“xiaoming = chenshu”,进行也是“复制的操作”, 为什么? 且看下图分解。

根据代码,首先, 申明了变量“chenshu” ,如下图3。

图 3

接下来, “xiaoming = chenshu”(复制操作),内存空间图如下图4

图4

在上图4 中,xiaoming 变量的存储空间(栈区)复制了 chenshu变量的值,

但是这个变量的值,并不是一个基础的数据类型,

而是一个堆区的内存地址(指针)。

所以操作“xiaoming”变量和操作“chenshu”变量效果都一样。


划重点:在JS的变量传递中,本质上都可以看成是值传递,

只是这个值可能是基础数据类型,也可能是一个指针,

如果是指针,我们通常就说为引用传递。

JS中比较特殊,不能直接操作对象的内存空间,

必须通过指针(所谓的引用)来访问。


所以,即使是所以复杂数据类型(对象)的赋值操作,

本质上也是值传递。在往下看看一个例子。


三、函数的形参、实参的值传递

看如下代码。


function setName(user){      user.name = "new name";// 重新设置name 这个属性}var chenshu = { name:"晨叔" };setName(chenshu);console.log(chenshu.name);


讲解:

函数 setName 有一个 形参 “user”,没毛病。

将“chenshu”对象传给 “setName ” 函数,chenshu 为实参,也没毛病。

重点:传参的过程中,js引擎将实参chenshu的值(值是对象{name:"晨叔"}的指针)“复制”给形参。

即,形参user 和 chenshu变量指向同一个堆内存对象。 没毛病。

但问题来了,实参和形参在是一个变量?还是两个变量?

很多开发者的误区:认为 在 setName 函数中改变了形参user的属性,

实参 chenshu的属性也发生变动,就认为同一个变量,

但真实的情况是:实际上实参和形参 是两个变量,只是实参和形参指向同一个堆区的变量而已,见如下图5


图5

形参user只是存了对象 { name:"晨叔" }的地址,

但在栈区中,user是单独存在的,而且函数运行完,

user 立即被释放了,为了更加直观的说明这个问题。

我们再来分析一段代码。


 function setName(user)   {     user.name = "new name";// 重新设置name 这个属性     user = { name:"西门吹雪" };   }  var chenshu = { name:"晨叔" };   setName(chenshu);   console.log(chenshu.name);//输出  new name

上面这段代码就能很能说明问题,在“setName”函数中,

形参user首先修改实参chenshu对象的name属性之后,

又重新指向了一个新对象“{ name:"西门吹雪" }”,

但因为 形参user和实参chenshu是两个对象,

形参user指向新对象后,对实参chenshu并没有影响,

因为实参和形参进行的是值传递(复制),实参和形参是两个独立在栈区的变量


本文的分享就到这里,我们来做一下总结。

我们初步理解了js中的堆区和栈区。理解了变量传递的方式——值传递,所谓的引用传递,本质上也是值传递,只是传递的这个值是一个指针, 值传递带来的效果就是在内存栈区创建了一个新的空间。通过函数的实参和形参传递的分析,我们进一步的理解js的值传递。


wx公众号——晨叔周刊。每周与晨叔深入研究一个话题,

每周一发布本周话题,周二,周六 早上九点更新周刊内容。

口号: 晨叔周刊,每周一话题,技术天天涨。








标签: #js二值化