首先我们知道JS中的数据类型大致可以分为简单数据类型和复杂数据类型;
当我们声明一个变量并给它赋值时,可以赋给其简单值和复杂值(以下堆内存和栈内存的地址表示均随意取的,只是为了区分,不代表真实的内存地址);
针对简单数据类型:
例1
var simpleData1 = 18var simpleData2 = simpleData1simpleData2 = 19console.log(simpleData1 ) //打印出18
以上例子的过程为:先给simpleData1 在栈内存中分配一个地址s101,里面保存数字18,然后将simpleData1赋给simpleData2 ,相当于紧接着在栈内存中又分配一个地址s102,里面同样保存数字18,这时再将s102中的内容变成19,这当然不会影响s101中的内容。
针对复杂数据类型,以数组为例
例2
var arr1 = ['zyp','zyp','zyp']var arr2 = arr1arr2[0] = 'zyp1'console.log(arr1) //打印出 ["zyp1", "zyp", "zyp"]
以上例子的过程为:先在堆中开辟一块地址h1001来存数组[‘zyp’,‘zyp’,‘zyp’],同时将该堆内存的地址h1001存放在栈内存s103中,然后再在栈内存中开辟一个地址s104同样存放h1001,这时arr1和arr2指向同一块堆内存,改变其中的值会同时反映在变量arr1和arr2上。
例3
var arr1 = ['zyp','zyp','zyp']var arr2 = arr1arr2 = ['zyp1','zyp1','zyp1']arr2[0] = 'zyp2'console.log(arr2) //打印出 ["zyp2", "zyp1", "zyp1"]console.log(arr1) //打印出 ["zyp", "zyp", "zyp"]
以上例子的过程为:跟上面一样,先在堆中开辟一块地址h1001来存数组[‘zyp’,‘zyp’,‘zyp’],同时将该堆内存的地址h1001存放在栈内存s103中,然后再在栈内存中开辟一个地址s104同样存放h1001,这时arr1和arr2指向同一块堆内存。不同的是:这时我们又在堆内存中开辟出一块新的地址h1002来存放新数组[‘zyp1’,‘zyp1’,‘zyp1’],新地址h1002存放在s104中,这时arr2中保存的地址已经由h1001变成了h1002, 这时再改变h1002对应的堆内存中的值,便和arr1没有任何关系了。
明白了上述赋值过程再来理解函数传参便是一个道理了
function changeObj(arr1) {arr1= ['a1', 'a1', 'a1']}var arr = ['a', 'a', 'a']changeObj(arr)console.log(arr) // 打印出["a", "a", "a"]
这个例子中:假设数组[‘a’, ‘a’, ‘a’]在堆内存中对应的地址为:h2001, 实参arr在栈内存中对应的地址为s201, 里面保存的是h2001。调用changeObj函数时,将arr复制给arr1,这里对应的操作是自动将在栈内存中分配一块地址s202来保存h2001,因此arr和arr1对应的是同一块堆内存,但这时又在堆内存中开辟新空间h2002来保存数组[‘a1’, ‘a1’, ‘a1’],h2002保存在arr1对应的栈内存s202中,这里的道理就和例3是一样的了