1、传递参数
ECMAScript 中所有函数的参数都是按值传递
的。这意味着函数外的值会被复制到函数内部的参数
中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是
引用值,那么就跟引用值变量的复制一样。对很多开发者来说,这一块可能会不好理解,毕竟变量有按
值和按引用访问,而传参则只有按值传递
。
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用 ECMAScript 的话说,
就是 arguments 对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变
量,这意味着对本地变量的修改会反映到函数外部。(这在 ECMAScript 中是不可能的。)来看下面这个
例子:
```js
function addTen(num) {
num += 10;
return num;
}
let count = 20;
let result = addTen(count);
console.log(count); // 20,没有变化
console.log(result); // 30
这里,函数 addTen()有一个参数 num,它其实是一个局部变量。在调用时,变量 count 作为参数
传入。count 的值是 20,这个值被复制到参数 num 以便在 addTen()内部使用。在函数内部,参数 num
的值被加上了 10,但这不会影响函数外部的原始变量 count。参数 num 和变量 count 互不干扰
,它们
只不过碰巧保存了一样的值。如果 num 是按引用传递的,那么 count 的值也会被修改为 30。这个事实
在使用数值这样的原始值时是非常明显的
。但是,如果变量中传递的是对象,就没那么清楚了。比如,
再看这个例子:
function setName(obj) {
obj.name = "Nicholas";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
这一次,我们创建了一个对象并把它保存在变量 person 中。然后,这个对象被传给 setName()
方法,并被复制到参数 obj 中。在函数内部,obj 和 person 都指向同一个对象。结果就是,即使对象
是按值传进函数的,obj 也会通过引用访问对象。当函数内部给 obj 设置了 name 属性时,函数外部的
对象也会反映这个变化,因为 obj 指向的对象保存在全局作用域的堆内存上
。很多开发者错误地认为,
当在局部作用域中修改对象而变化反映到全局时,就意味着参数是按引用传递的。为证明对象是按值传
递的,我们再来看看下面这个修改后的例子:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
这个例子前后唯一的变化就是 setName()中多了两行代码,将 obj 重新定义为一个有着不同 name
的新对象。当 person 传入 setName()时,其 name 属性被设置为"Nicholas"。然后变量 obj 被设置
为一个新对象且 name 属性被设置为"Greg"。如果 person 是按引用传递的,那么 person 应该自动将
指针改为指向 name 为"Greg"的对象。可是,当我们再次访问 person.name 时,它的值是"Nicholas",
这表明函数中参数的值改变之后,原始的引用仍然没变
。当 obj 在函数内部被重写时,它变成了一个指
向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了
。
注意 ECMAScript 中函数的参数就是局部变量。
摘自《你不知道的JavaScript》
下面是例子
例子引用他人文章,这里我做个抛砖引玉之人,这个问题的确不是很好理解。
1、
function test(m) {
m = {v : 5};
}
var m = {k : 30};
test(m);
alert(m.v); // undefined
2、
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
3、
var obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1
如果 JavaScript 采用的是引用传递,外层的值也会被修改呐,这怎么又没被改呢?所以真的不是引用传递吗?
这就要讲到其实还有第三种传递方式,叫按共享传递。
而共享传递是指,在传递对象的时候,传递对象的引用的副本。
注意: 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!
所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以第二个和第三个例子其实都是按共享传递。
最后,你可以这样理解:
参数如果是基本类型是按值传递,如果是引用类型按共享传递。
但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了。
所以,高程,谁叫你是红宝书嘞!
里面有很多讨论,建议还是看原文
附上原文