JS参数传递方式

2017-08-15

JS参数都是按值传递的

在高程中是这样描述的:

ECMAScript中所有函数的参数都是按值传递的

我们怎么理解这句话呢?

几个例子 🌰

var i = 1;
function test(k) {
    k = 2
    console.log(k)
}
test(i)  //2
console.log(i) //1
var o = {
    value: 1
}
function test(o) {
    o.value = 2
    console.log(o.value)
}
test(o) //2
console.log(o.value) //2
var o = {
    value: 1
}
function test(o) {
    o = 2
    console.log(o)
}
test(o) //2
console.log(o.value) //1
  1. 我们先看第一个例子,很好理解,按值传递,当 i 被传进 test 方法,相当于拷贝了一份 i , 设这个值为 _i ,我们后续修改的都是 _i ,而不会影响原值

  2. 再看第二个例子,把 o 传进 test 方法,传递的是地址值(即引用),所以当改变 o 的 value 的时候,源对象也会被影响,因为两者是引用的同一个地址

  3. 按照上面的说法,此处修改了 o 的值,源对象也应该被修改啊,然而源对象并没有变,是哪 里出错了吗?难道说 js 并不是按值传递吗?

进一步思考

我们知道, js 数据类型分为两种:值类型和引用类型,值类型存储在栈中,引用类型存储在堆中, 同时维护一个地址在栈中。而参数传递方式就是指实参传递给形参的一个过程。值传递就是将实参在 栈中的值传给

从这个角度来看三个例子:

  1. 在栈中申请一个地址 001 ,给它一个值 1,将这个值传递到 test 方法,为形参 o在栈 中申请一个地址 002, 将 1赋给 002,所以修改该值的时候,修改的是 002 地址的 值,001 的值并没有发生改变。所以再次打印,依然是 1

  2. o 是引用类型,所以需要在堆中申请一个地址 011,保存一个对象(属性 value 为 1), 然后在栈中申请一个地址 001 ,这个地址保存了 011 这个内存地址值,然后为 test 方法 的形参 o 在栈中申请一个地址 002 ,它保存了 011 这个内存地址值,所以当修改它的值 的时候,会去找 011 这个地址的对象。所以再次打印 o 的时候,它的值已经发生了变化

  3. o 是引用类型,在堆中申请一个地址 011 ,保存一个对象(属性 value 为 1),然后 在栈中申请一个地址 001 ,这个地址保存了 011 这个内存地址值,然后 test 方法的形参 o 在栈中申请一个地址 002,它保存了 011这个内存地址值,到目前为止跟第二个例子 都是一样的,接下来不同的地方来了,它给形参 o 赋值为 2,由于 2 是 Number 类型, 所以直接给 002 这个地址赋值为 2,但是源对象在堆中的值并没有改变,所以第一次打印 为 2,第二次打印仍然为 1

结论

大家不要纠结于是值传递还是引用传递或者是 call by sharing。只要搞懂上面的几个概念, 就能清楚了解js参数的传递方式

锁窗前月明色, 雕阑外夜气清