九、JavaScript之参数按值传递
定义
在《JavaScript 高级程序设计》中讲到传递参数:
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。对很多开发者来说,这一块可能会不好理解,毕竟变量有按值和按引用访问,而传参则只有按值传递。
按值传递
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用 ECMAScript 的话说,就是 arguments 对象中的一个槽位)。
来看一个例子:
1 | function foo(num) { |
这里,函数 foo()有一个参数 num,它其实是一个局部变量。在调用时,变量 count 作为参数传入。count 的值 20 被复制给参数 num 。在函数内部,参数 num 的值被加上了 10,但这不会影响函数外部的变量 count。参数 num 和变量 count 互不干扰,它们只不过碰巧保存了一样的值。
引用传递
众所周知按引用传递接收的不是值拷贝,而是对象的隐式引用,如该对象在外部的直接引用地址。函数内部对参数的任何改变都是影响该对象在函数外部的值,因为两者引用的是同一个对象,也就是说:这时候参数就相当于外部对象的一个别名。
伪代码:
1 | var obj = { |
哪ECMAScript 中函数传递引用数据类型参数是引用传递吗?然而并不是,除了上面两种策略以外,我们看看第三种策略。
共享传递
共享传递策略还有一些代名词:“按对象传递”或“按对象共享传递”。该策略是1974年由Barbara Liskov为CLU编程语言提出的,策略的要点是:
函数接收的是对象引用的拷贝(副本),该引用拷贝和形参以及其值相关联。
这里出现的引用,我们不能称之为“按引用传递”,因为函数接收的参数不是直接的对象别名,而是该引用地址的拷贝。
最重要的区别就是:函数内部给参数重新赋新值不会影响到外部的对象(和上例按引用传递的case),但是因为该参数是一个地址拷贝,所以在外面访问和里面访问的都是同一个对象,改变该参数对象的属性值将会影响到外部的对象。
1 | var person = { |
这里,我们创建了一个对象 person 并这个对象被传给 foo 方法,并被复制到参数 obj 中。在函数内部,obj 和 person 都指向同一个对象。个人觉得引用数据类型传递的也是值, 不过这个值是一个地址指针,
我们再来看看下面这个修改后的例子:
1 | var person = { num: 1 }; |
当 person 传入 foo()时,其 num 属性被设置为 2。然后变量 obj 被设置为一个新对象且 num 属性被设置为 3。如果 person 是按引用传递的,那么 person 应该自动将指针改为指向 num 为 3 的对象。可是,当我们再次访问 person.num 时,它的值是 2,这表明函数中参数的值改变之后,原始的引用仍然没变。当 obj 在函数内部被重写时,它变成了一个指向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了。
总结
- 如果参数是基本类型,那么是按值传递。
- 如果参数是引用数据类型,传递的还是值,但是这个值是引用数据类型地址的拷贝。