JS中引用型变量在函数形参里的表现
昨天快手面试,面试官出了这样一道题:
var a = [1, 2, 3, 4]; function set(a) { a = [5, 6, 7, 8]; } set(a); console.log(a);
首先对于基本数据类型,函数里面会创建一个新的(地址不同的)变量,对形参的修改不会影响实参,比如:
let num = 2; function modify_number(num) { num = 10; } modify_number(num) console.log(num);
显然,打印结果为2
但是对于引用型变量,传入的是指针,函数内可以修改实参内容(注意是内容,比如修改某个键的值,数组的第二个元素),以下代码是可以实现的:
function fn1(fnObj) { fnObj.a = 2; } let obj = { a: 1 } fn1(obj); console.log(obj.a); // 2
所以我就理所当然的认为:行参和实参其实就是同一个变量,但其实不然。
再看个例子:
function fn1(fnObj) { fnObj = { a: 2, b: 1 } } let obj = { a: 1 } fn1(obj); console.log(obj); // {a: 1}
可以看到obj并没有被修改
背后真正的逻辑是,行参和实参是两个变量,只是里面存储的指针相同,指向的地址相同,所以对值的修改是完成了
如下面这个图所示:
obj和fnObj其实是两个不同的变量(在栈中的地址不同),但是里面存储的指向堆堆地址是相同的,所以修改键的值会生效
而对于直接重新赋值的情况,由于赋值符号是从右往左进行的,所以先生成了一个新对象(姑且称为temp),指向address2
赋值之后,fnObj的值就从address1变成了address2,可以看出整个过程都和obj没有关系
总结:
这个题目其实不算难,只要厘清了引用型变量堆栈存储的关系以及函数中行参和实参的关系,就没有问题了