ECMAScript中所有函数的参数都是按值传递的,简单讲就是函数外部的值 复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。切记访问变量有按值访问和按引用访问,而参数只能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(可以把ECMAScript函数中的参数想象成局部变量);向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量。
实例1:
// 参数是基本类型的值 function add(n){ n += 1; return n; } var num = 2; var result = add(num); alert(num); // 2 alert(result); // 3
在函数内,参数n的值被加上1,但这并不会影响函数外的num变量,参数n与变量num仅仅是具有相同值,但互不相识。如果是按引用传递,则变量num的值将变为3.
实例2:
// 参数是对象 function setName(obj){ obj.name = "123"; } var p = new Object(); setName(p); alert(p.name); // "123"
这里创建了一个对象,并保存在变量p中,当这个对象被传递到setName()函数中之后就被复制给了obj。在函数内,obj和p引用的是同一个对象。也可以说即使这个对象是按值传递的,obj也会按引用来访问同一个对象。所以在函数内为obj添加name属性后,函数外的p会反映出来,这里p指向的对象在堆内存中只有一个,并且是全局对象。
很多错误地认为在局部作用域中修改的对象会在全局作用域中反映出来,就说明是按引用传递。为了证明上面例子是按值传递,再看下面的实例3:
function setName(obj){ obj.name = "123"; obj = new Object(); // obj的值变为堆内存中一个新的地址,但o变量的值并不受影响 obj.name = "321"; } var p = new Object(); setName(p); alert(p.name); // "123"
这里与实例2中不同的是给obj重新定义了一个对象,并重新定义了name属性。如果p是按引用传递的,那么p就会自动被修改为指向name属性值为“321”的新对象,但实际访问p.name时,值仍是“123”。这就说明虽然在函数内修改了参数的值,但原始变量的引用不会受影响,保持不变。其实当参数是对象时,按值传递时传递的就是对象在堆内存中的地址 另:当在函数内部重写obj时,这个变量引用的就是一个局部对象了,而这个局部对象在函数执行完后立即被销毁。
在Java中也类似,以下是简单的实例:
public class Demo14 { public static void main(String[] args) throws Exception{ int num = 2; int result = add(num); System.out.println(num); // 2 System.out.println(result); // 3 Obj o = new Obj(); setObjName(o); System.out.println(o.getName()); // 123 } public static int add(int n){ n += 1; return n; } public static void setObjName(Obj obj){ obj.setName("123"); obj = new Obj(); // obj的值变为堆内存中一个新的地址,但o变量的值并不受影响 obj.setName("321"); } } class Obj{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }