parameter&argument
形式参数(parameter)是函数定义时的参数;
实际参数(argument)是函数调用时实际传入的参数。
let n1 = 10;
let a1 = [ 'a', 'b', 'c' ];
function test(num, arr) {
num = 20;
arr[1] = 'z';
console.log(num); // 20
console.log(arr[1]); // z
}
test(n1, a1);
声明函数时,test(num, arr)
<= 这里num
和arr
就是test函数的形式参数(parameter)
调用函数时,test(n1, a1)
<= 这里n1
和a1
就是调用时传入的实际参数(argument)
arguments对象
arguments对象用于获取函数调用时的实际参数(argument);
需要注意:arguments是一个对象,而不是数组;
它具有length属性,它的长度由实参数量决定;
let n1 = 10;
let a1 = [ 'a', 'b', 'c' ];
function test() {
arguments[0] = 20;
arguments[1][1] = 'z';
console.log(arguments[0]); // 20 <= 运行结果没有改变
console.log(arguments[1][1]); // z <= 运行结果没有改变
}
test(n1, a1);
上面的栗子可以看出:
- 用arguments直接代替形参,函数运行的结果没有改变;
- 形式参数(parameter)可以少于实际参数(argument),甚至可以没有。
实际上,形式参数(parameter)与实际参数(argument)并没有直接关系,实参少于形参也是可以的,未匹配的形参将被赋值undefined
;
let n1 = 10;
let a1 = [ 'a', 'b', 'c' ];
function test(num, arr, a) {
num = 20;
arr[1] = 'z';
console.log(num); // 20 <= 运行结果没有改变
console.log(arr[1]); // z <= 运行结果没有改变
console.log(a); // undefined <= 未匹配的形参
}
test(n1, a1);
传值与传址
上面的栗子有一个有趣的现象,打印函数执行后的n1
和a1
发现,n1
的值没有改变,而a1
却被改变了,n1
传进函数作为局部变量仅在函数内部改变,而全局变量a1
却被改变了。
let n1 = 10;
let a1 = [ 'a', 'b', 'c' ];
function test(num, arr) {
num = 20;
arr[1] = 'z';
console.log(num); // 20
console.log(arr[1]); // z
}
test(n1, a1);
console.log(n1); // 10 <= n1没有被改变
console.log(a1[1]); // z <= a1[1]被函数改变了
如何理解传值与传址呢?看两个更为直接的栗子吧。
传值(pass-by-value)
let a = 1
let b = a;
b = 9;
alert(a); // 1
a的值没有变化,在这个栗子中,a的值赋给b,然后得到a赋值的b被赋值为9,但最后a的值并没有发生改变;
a的值被操作过程中,对实际的值做了一份复制,这份复制存在b中
复制的值和原来的值是两份独立的值,修改复制的值,原值自然不会发生改变。
传址(pass-by-address)
let objA = new Object();
objA.value = 1;
let objB = objA;
objB.value = 9;
alert(objA.value); // 9
两个相同的例子,只是把数据类型换成了对象,就有了不一样的结果;
如果按照传值的逻辑,这里修改的是objB的值,为什么objA也跟着变了呢?
let objB = objA;
将objA的值赋给objB,同样也会复制一份objA的原值存到新变量objB的空间中;
不同的是,这个值的副本实际上是一个指针,指针指向原值的对象,当改变其中任一个变量(或对象的属性)时,另一个的值也会发生变化。
结论
JS的参数是按值传递的;
如果参数传的是基本类型
的值如(Number, Boolean, String),那么传的是值的副本,副本的值与原值没有直接联系;
如果传的是引用类型
的值如(Object, Array, Function),那么传递的其实是指针
,当改变其中任一个变量(或对象的属性)时,另一个的值也会发生变化。
特殊情况:
/* null虽然是object,但它不占内存,地址为空所以是传值 */
let n = null;
let b;
b = n;
b = 2;
alert(n); // null
/* 这种情况弹出1,2,3是因为给arr2重新赋值而不是对现有的arr2[x]进行操作 */
let arr = [ 1, 2, 3 ]; // eaqual: let arr = new Array(1,2,3);
let arr2 = arr;
arr2 = []; // eaqual: let arr2 = new Array();
alert(arr); // 1,2,3