按值传递和按地址传递
变量分为基本类型(值型)和引用类型变量. 基本数据类型包括数字,字符串,布尔,null,undefined这些, 引用数据类型包括object(Object,Array, RegExp, Date, Math...), 函数.
如果将基本类型的值赋值给变量, 变量会将这个值的本身保存起来:
var a = 123; // 将数值123赋给变量a
var b = a; // 将变量a的值(数值123)赋给变量b
此时如果将变量b自增操作, 变量a的值不会受到影响;
b++;
console.log(b); // 124
console.log(a); // 123
如果将对象赋值给变量, 实际上是把对象的引用赋值给了变量; 变量保存的只是对象在内存中的地址.
var a = {x:1, y:3};
var b = a; // 将对象的地址赋值给b
上面的代码, 变量a和b都指向相同的地址, 也就是引用了同一个对象; 如果对b所引用的对象进行改变, 这一改变也会体现在变量a之中;
b.x++;
console.log(b.x); // 2
console.log(a.x); // 2
接下来看这段代码:
var a = {x:1,y:2};
var b = a; // b和a指向同一个对象
a = {x:2,y:1}; // a重新赋值,指向另一个对象
console.log(b.x);
这段代码通过赋值运算符将a重新赋值, 于是a不再引用一开始的对象, a与b之间的联系被切断, 因此不会再互相影响.
函数的参数(按值传递)
ECMAScript中所有函数的参数都是按值传递的;
function swap(a,b) {
var tmp = a;
a = b;
b = temp;
}
var one = 1;
var zero = 0;
swap(one, zero);
console.log(one, zero); // one与zero的值没有发生改变
把函数外部的值赋值给函数内部的参数, 相当于把值从一个变量复制给另一个变量一样. 函数的参数a和b可以理解成函数的私有变量,它们被赋于了全部变量one和zero的值, 然而这两个函数内部变量赋值的改变影响不到全部变量one和zero.
如果使用对象, 情况又会有所不同:
function setName(obj) {
obj.name = "Nicolas";
}
var person = new Object();
setName(person);
console.log(person.name);
以上代码创建了一个对象, 将对象保存到变量person中. 变量person被传递到函数setName后被复制给了obj, 这一步可以理解成, 对象的引用地址被复制给了obj. obj和person引用了同一个对象. 于是, 当在函数内部为obj添加name属性后, 函数外部的person也会有所反映.有的人会认为如果把引用类型数据作为参数, 参数就是按引用传递的. 其实无论传递了什么, 全都是按值传递. 改写下上面的代码:
function setName(obj) {
obj.name = "Nicolas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name); // "Nicolas"
这个例子中在函数内部创建了一个新的object对象并将name属性设置为"Greg", 如果person是按引用传递的, person就会被修改了name属性为"Greg"的新对象; 而事实上并没有. 说明即使在函数内部修改了参数的值, 也影响不到原始引用. 实际上函数内部重写的obj是引用了一个只在函数局部作用域中存在的对象, 函数执行完毕后这个对象就会被销毁.
本文参考了javascript高级程序设计第3版和javascript编程全解(然而写得比原书乱多了...)