本次尝试经过流程图的形式并结合两个例子来从新理解一下JavaScript中的参数传递。git
欢迎关注个人博客,不按期更新中——github
借用红宝书的一句话:函数
ECMAScript中全部函数的参数都是按值传递的
这个值若是是简单类型,那么就是其自己。若是是引用类型也就是对象传递的就是指向这个对象的地址。故咱们能够认为参数传递所有都是值传递,那么具体怎么理解呢?看下例子:spa
var obj = { n: 1 }; function foo(data) { data = 2; console.log(data); //2 } foo(obj); console.log(obj.n) // 1
先不说为何缘由,咱们就经过画图的方式来走一遍流程,我相信应该就能理解其中的参数传递了。切记传递引用类型传递的是指针!
首先执行var obj = {n: 1};
,能够看做在栈的001地址中存入了一个指向{n:1}
的指针*p3d
接下来为声明function foo
此时会建立函数执行上下文,产生一个变量对象,其中声明了形参data,因为函数没有执行,当前值为undefined。咱们记data地址为022。关于更多变量对象的知识能够参考冴羽老师的这篇JavaScript深刻之变量对象,本文不深刻研究关于AO相关,你只须要知道在声明这个函数的时候里面的形参已经被建立出来了。
执行foo(obj)
其中会进行参数传递,其中将obj中存储的*p拷贝给处在022地址的data,那么此时它们就指向了同一个对象,若是某一个变量更改了n的值,另外一个变量中n的值也会更改,由于其中保存的是指针。指针
进入函数内部,顺序执行data = 2;
此时002地址存储了基本类型值,则直接存储在栈中,从而与堆中的{n:1}失去了联系。从而打印console.log(data) // 2
,最后发现初始开辟的{n:1}对象没有过更改,故而 console.log(obj.n) // 1
仍然打印1。code
var obj = {n:1}; (function(obj){ console.log(obj.n); //1 obj.n=3; var obj = {n:2}; console.log(obj.n) //2 })(obj); console.log(obj.n) //3
总体来看这个例子中出现了同名覆盖的问题。不太了解代码如何执行的流程,可能会由于同名的关系而有些混乱,不过不要紧。只要按照上一个例子的流程图中的执行过程,必定能够得出正确的结果。对象
声明变量obj,地址为011其中存入指向{n:1}的指针*pblog
声明函数,虽然同为obj变量名,可是形参obj为AO中的属性,不会与全局形成覆盖,其拥有新的地址记做022,在未执行前其值为undefined。ip
函数当即执行,此时将全局obj赋值给形参obj,咱们忽略这个重复命名的问题,其实就是将011中的 指针*p拷贝了一份给了022。同时执行第一个console.log(obj.n)
结果即为1。
执行obj.n=3
,此时为函数的形参即022中的obj来改变了对象内n的值。
最关键的一步:var obj = {n:2};
因为对象命名的关系可能不少童鞋就会有点懵,但依然按照一样的方式来分析便可,因为使用了var那么就是新声明一个对象,从而会在栈中压入新的地址记做033,其中存入了新的指针指向了新的对象{n:2}。从而以后打印的console.log(obj.n)
结果则应是新开辟的对象中的n的值。
最后打印 console.log(obj.n) //3
很显然,全局的对象有过一次更改其值为3。
至此咱们走完了上述两段代码涉及变量的全部“心路历程”,因为做者不是科班出身,这个图中对于堆栈以及变量重名的描述可能不是很是的准确,有差错的地方还望不吝赐教~重点是能理解我但愿表达的意思就好。总的来讲关键点就在于传参的过程当中存在一次值的拷贝,同时若是赋值对象是引用类型传入的是指针,明白这两点以后再加上以前流程图的分析相信再遇到相似的问题均可以有较为一致的思路了。
惯例po做者的博客,不定时更新中——有问题欢迎在issues下交流。