关于 js 函数返回值的一个小问题

2020-04-24 09:58:55 +08:00
 ynohoahc

也算是吃前端这碗饭吃了一年多了, 但是今天遇到个基础问题解决不了, 很惭愧 因为公司网络,不能外网传文件(图片), 我就用伪代码稍微代替一下我的问题场景

function test(list) { list = list.slice(1) }

a = [0,1,2,3,4,5] test(a)

这样子我预想 a 应该是等于[1,2,3,4,5]了, 但实际上还是[0,1,2,3,4,5]

2672 次点击
所在节点    问与答
45 条回复
Vegetable
2020-04-24 10:44:24 +08:00
@leoskey #13 没有拷贝吧,只是两个不同的地址指向了同一个数组,当再次赋值的时候,list 这个地址指向了 slice 方法产生的新数组,a 这个数组完全没有变过.

如果直接操作传入的 list,外边的 a 是会改变的.
LyleRockkk
2020-04-24 10:47:34 +08:00
@max21 这位老哥正解了,数组也是引用传递的,slice 返回了新数组,并没有改变 a

var a = [1,2,3];
function test(list){
list.push(4);
}
test(a);

这样,a 就会变了
ynohoahc
2020-04-24 11:01:25 +08:00
@LyleRockkk #22 啊? 我个人并不认可 max21 老哥的说法, 我知道 slice 不是变异方法. "对象都是引用传递的"这句话我不知道是什么场景下说的, 但是至少在函数传参的场景中,按照 javascript 高程所说, 都是"值传递"的
https://i.loli.net/2020/04/24/UkoxRCSygI5jKPO.jpg

所以我传给 test 的只是 a 这个变量代表的内存地址所代表的的值,
当我在内部执行完 list = list.slice(1)后, 实际上只是内部的形参 list 所代表的内存地址所代表的值变了一下而已, 跟外部的实参 a 实际上没任何关系.

这是我理解的
yaphets666
2020-04-24 11:07:02 +08:00
面试经常问的 哪些数组方法是改变原数组的 哪些方法不改变原数组
redam
2020-04-24 11:19:50 +08:00
一点浅见:
js 函数参数是值传递指的是,函数参数获得传入变量的一份复制。实际上变量引用地址也是复制了的。楼主代码没有生效的原因是,list 进行了重新的赋值,改变了其引用,所以在函数内的修改,外部变量 list 并没有改变,稍微改造一下,不改变 list 的引用就可以生效了:

```
function test(list) {
let _list=list.slice(1);
list.length=0;
list=list.push(..._list);
}


let a = [0, 1, 2, 3, 4, 5];

test(a);
console.log(a); //[ 1, 2, 3, 4, 5 ]
```
wildnode
2020-04-24 11:20:59 +08:00
说下我个人的理解哈,JS 中函数参数确实都是值传递的,但是对于复杂类型(对象,数组),这个值传递中的值指的是变量的内存地址,而不是变量本身,比如楼主的例子,你把 slice 换成 pop,list 是会变的,这说明并不是楼上一些 V 友所说的"值传递"。第二个问题,既然是传递的内存地址,为什么函数中给 list 赋值后,list 没有发生变化呢,这里我觉得应该是在函数的作用域中对形参赋值,会改变形参的地址,但是并不会对之前地址中的变量产生影响。

Emmmm,感觉我也说不明白,抛砖引玉等大佬吧。
max21
2020-04-24 11:22:13 +08:00
@ynohoahc a 保存着 [0,1,2,3,4,5]的引用,执行 test 函数后,a 赋值给 list,那么这时 a 和 list 同时保存[0,1,2,3,4,5]的引用。然后 list = list.slice(1),list.slice(1)返回一个新的对象然后赋值给 list,这时 list 更改了,但这里并没有更改 a,a 还是保存了[0,1,2,3,4,5]的引用,然后函数执行完 list 被回收了,
wutiantong
2020-04-24 11:29:20 +08:00
能遇到并且问出这个问题,至少标志着 lz 的编程能力即将入门了
shintendo
2020-04-24 11:34:54 +08:00
@max21
@LyleRockkk
js 只有值传递,没有引用传递
实际上如 @leoskey 所说,正因为不是引用传递,所以楼主的代码才无效
LyleRockkk
2020-04-24 11:46:16 +08:00
@ynohoahc em... 我说的引用传递,是我理解的,对引用类型的值的传递。 图里面书说的很准确,用的多了,容易有自己的说法,还好理解没跑偏
ryncv
2020-04-24 11:55:36 +08:00
通常所说的 js 引用传递,是因为复杂类型的值其实就是引用啊。
声明 const a = [1,2]; 数组 [1,2]被保存在堆内存,a 是一个引用地址。
在调用时 function(a){},a 是一个引用的值没毛病啊。
原来 a -> [0,1,2,3,4,5]
关键原因还是 [list.slice 返回了一个新数组] 。函数内的 list=list.slice 后,list 指向了一个新的数组[1,2,3,4,5],所以原数组不变。
不信试试下面这个:
function test(list) { list = list.pop() }
原来的 list 一定被改了。
noe132
2020-04-24 12:00:45 +08:00
variable holds the reference to the object

function call test(a) pass the reference of a to parameter variable list

a and list holds the same reference but they are different variable
wutiantong
2020-04-24 12:03:12 +08:00
@ryncv 把你的例子改成 function test(list) { list.pop() } 会好很多,不必要的那次赋值会让搞不懂的人更加困惑。
leihongtao1230
2020-04-24 12:05:32 +08:00
我来回答一下,对于函数的参数是复杂类型的,传递参数是复制引用传递,也就是把内存地址复制一份给参数,也就是你用这个地址修改原来的数据可以,但是你把这个参数重新赋值就不会影响原来的对象
ryncv
2020-04-24 12:09:44 +08:00
@wutiantong 是的,感谢指正,写快直接把 lz 例子抄过来了。
auroraccc
2020-04-24 12:19:05 +08:00
a 和 list 都是指向[0,1,2,3,4,5]的指针,函数内部把 list 的指向[1, 2, 3, 4, 5],并不会更改 a
isyuu
2020-04-24 14:33:49 +08:00
据我所知除了 C/C++的 应用传递(&), c#的 ref 关键字, 其他 java/js/python 这么写都没卵用吧, 你对多只是对 list 这个局部指针赋值而已...
leoskey
2020-04-24 14:45:01 +08:00
@shintendo
@Vegetable 这位解释的是正确的,a 与 list 是不同地址指向同一数组,对 list 赋值改变了 list 的地址
yeqizhang
2020-04-24 14:45:30 +08:00
和 java 的差不多,以前我也干过去改变传入的对象的引用的事。
你要是函数 return list,a=test(a)就好
raistlin916
2020-04-24 15:11:59 +08:00
slice 是生成新的,splice 是改原值。这里有困惑很正常,api 自己都没统一行为

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/665550

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX