也算是吃前端这碗饭吃了一年多了, 但是今天遇到个基础问题解决不了, 很惭愧 因为公司网络,不能外网传文件(图片), 我就用伪代码稍微代替一下我的问题场景
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]
1
shintendo 2020-04-24 10:14:44 +08:00 1
list 是局部变量,你在函数里给 list 赋值毫无意义。这应该是编程常识吧,跟 js 没什么关系
|
2
max21 2020-04-24 10:16:56 +08:00 1
function test(list) { return list.slice(1) }
|
3
hewelzei 2020-04-24 10:22:24 +08:00
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
slice() 是返回新的数组,不改变原来的数组,应该仔细看看 MDN 文档,补充一下基础知识 |
4
mc201319 2020-04-24 10:23:54 +08:00 via Android
你这里是把 a 的值当做参数穿进去的,list 只是和 a 的值相同,他们的指针已经完全不同了
|
5
Vegetable 2020-04-24 10:23:58 +08:00 1
@shintendo #1 是因为这个?不是因为 slice 不是 in place 的?
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括 end )。原始数组不会被改变。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice 你这个需求应该是 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice Array.prototype.splice ```Javascript function test(list) { list.splice(0, 1) } const a = [0, 1, 2, 3, 4, 5] test(a) console.log(a) //[ 1, 2, 3, 4, 5 ] ``` |
6
Vegetable 2020-04-24 10:24:23 +08:00
拜托,我只是一个后端,都知道这个,前边的大哥都在干什么?
|
7
Vegetable 2020-04-24 10:26:28 +08:00
|
8
LyleRockkk 2020-04-24 10:27:08 +08:00
吃前端这碗饭一年多了...
|
9
maichael 2020-04-24 10:27:54 +08:00 1
@Vegetable #7 很好解释,js 函数传参都是值传递,外面的 a 和里面的 list 实际上处理值相同之外没什么关系。
|
10
wszgrcy 2020-04-24 10:29:54 +08:00 via Android
请加参数前面加& (滑稽)
|
11
LyleRockkk 2020-04-24 10:30:15 +08:00
LZ 应该是 想通过一个 function 改变 外部定义好的 a 的值,不过还是基础问题,不管在 function 内部,还是外部,都要有对 a 赋值的过程,才能改变 a
|
12
b821025551b 2020-04-24 10:31:21 +08:00 1
你这一年多的工作经历是切图么
|
13
leoskey 2020-04-24 10:31:46 +08:00
这里是 值传递,拷贝一份 a 传到 list,所以 a 和 list 是不同的。
|
14
TomVista 2020-04-24 10:32:58 +08:00
@Vegetable emm,上面讲的也有道理的,,另外 list = list.slice(1) 这个东西因为命名的原因语义并不太清楚,理解的不一样很正常
|
15
icebreaker12 2020-04-24 10:36:10 +08:00
其实利用浅拷贝把 a 改成对象就能实现了。
function test(list) { list.arr = list.arr.slice(1) } var a = { arr:[0,1,2,3,4,5] }; test(a); |
17
CharmanderS5 2020-04-24 10:37:35 +08:00 via Android
两个数组引用的地址不用 所以原数组不会被改变
|
18
ynohoahc OP @LyleRockkk #8 哈哈哈 划水了一年多
|
19
ynohoahc OP @b821025551b #12 哎 心灰意冷 实际上切图都不会切 都直接拿 UI 的
|
20
shintendo 2020-04-24 10:42:30 +08:00
@Vegetable 因为楼主写的是 list = list.slice(1)而不是 list.slice(1),所以我默认楼主知道这不是 in place 的
|
21
Vegetable 2020-04-24 10:44:24 +08:00
@leoskey #13 没有拷贝吧,只是两个不同的地址指向了同一个数组,当再次赋值的时候,list 这个地址指向了 slice 方法产生的新数组,a 这个数组完全没有变过.
如果直接操作传入的 list,外边的 a 是会改变的. |
22
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 就会变了 |
23
ynohoahc OP @LyleRockkk #22 啊? 我个人并不认可 max21 老哥的说法, 我知道 slice 不是变异方法. "对象都是引用传递的"这句话我不知道是什么场景下说的, 但是至少在函数传参的场景中,按照 javascript 高程所说, 都是"值传递"的
https://i.loli.net/2020/04/24/UkoxRCSygI5jKPO.jpg 所以我传给 test 的只是 a 这个变量代表的内存地址所代表的的值, 当我在内部执行完 list = list.slice(1)后, 实际上只是内部的形参 list 所代表的内存地址所代表的值变了一下而已, 跟外部的实参 a 实际上没任何关系. 这是我理解的 |
24
yaphets666 2020-04-24 11:07:02 +08:00
面试经常问的 哪些数组方法是改变原数组的 哪些方法不改变原数组
|
25
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 ] ``` |
26
wildnode 2020-04-24 11:20:59 +08:00
说下我个人的理解哈,JS 中函数参数确实都是值传递的,但是对于复杂类型(对象,数组),这个值传递中的值指的是变量的内存地址,而不是变量本身,比如楼主的例子,你把 slice 换成 pop,list 是会变的,这说明并不是楼上一些 V 友所说的"值传递"。第二个问题,既然是传递的内存地址,为什么函数中给 list 赋值后,list 没有发生变化呢,这里我觉得应该是在函数的作用域中对形参赋值,会改变形参的地址,但是并不会对之前地址中的变量产生影响。
Emmmm,感觉我也说不明白,抛砖引玉等大佬吧。 |
27
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 被回收了,
|
28
wutiantong 2020-04-24 11:29:20 +08:00
能遇到并且问出这个问题,至少标志着 lz 的编程能力即将入门了
|
29
shintendo 2020-04-24 11:34:54 +08:00
|
30
LyleRockkk 2020-04-24 11:46:16 +08:00
@ynohoahc em... 我说的引用传递,是我理解的,对引用类型的值的传递。 图里面书说的很准确,用的多了,容易有自己的说法,还好理解没跑偏
|
31
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 一定被改了。 |
32
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 |
33
wutiantong 2020-04-24 12:03:12 +08:00
@ryncv 把你的例子改成 function test(list) { list.pop() } 会好很多,不必要的那次赋值会让搞不懂的人更加困惑。
|
34
leihongtao1230 2020-04-24 12:05:32 +08:00 via Android
我来回答一下,对于函数的参数是复杂类型的,传递参数是复制引用传递,也就是把内存地址复制一份给参数,也就是你用这个地址修改原来的数据可以,但是你把这个参数重新赋值就不会影响原来的对象
|
35
ryncv 2020-04-24 12:09:44 +08:00
@wutiantong 是的,感谢指正,写快直接把 lz 例子抄过来了。
|
36
auroraccc 2020-04-24 12:19:05 +08:00
a 和 list 都是指向[0,1,2,3,4,5]的指针,函数内部把 list 的指向[1, 2, 3, 4, 5],并不会更改 a
|
37
isyuu 2020-04-24 14:33:49 +08:00
据我所知除了 C/C++的 应用传递(&), c#的 ref 关键字, 其他 java/js/python 这么写都没卵用吧, 你对多只是对 list 这个局部指针赋值而已...
|
38
leoskey 2020-04-24 14:45:01 +08:00
|
39
yeqizhang 2020-04-24 14:45:30 +08:00 via Android
和 java 的差不多,以前我也干过去改变传入的对象的引用的事。
你要是函数 return list,a=test(a)就好 |
40
raistlin916 2020-04-24 15:11:59 +08:00
slice 是生成新的,splice 是改原值。这里有困惑很正常,api 自己都没统一行为
|
41
zhouS9 2020-04-24 15:14:00 +08:00
list 和 a 指向同一个地址,使用 splice 这种方法,就会同时改变两者,但是 slice 不行
|
42
AnnaXia 2020-04-24 17:56:33 +08:00
写了个解释希望有说清楚,v2ex 的回复不显示别人回答内容,看起来太累了。不 @楼上正确解答的朋友了
a = [0,1,2,3,4,5] // a 指向数组 [0,1,2,3,4,5] function test(list) 并调用 test(a) // test 也指向数组 [0,1,2,3,4,5] list.slice(1) // slice 方法不改变原数组,生成一个新的数组 [1,2,3,4,5] list = list.slice(1) // list 指向改变,变为指向数组[1,2,3,4,5] test(a)后 console.log(a) // a 指向未变,原数组也没变,所以依然是 [0,1,2,3,4,5] 楼里有人建议 splice 或 pop,这个方案可行是因为改变了原数组 [0,1,2,3,4,5], a 指向未变,原数组改变为[1,2,3,4,5],所以可以得到想要的结果。 |
43
AnnaXia 2020-04-24 17:58:53 +08:00
|
44
autoxbc 2020-04-24 18:58:03 +08:00
JS 里只讲原始值和对象值,不讲指针,内存地址,乱用概念只会造成歧义和误解
|
45
lewinlan 2020-04-25 00:28:15 +08:00 via Android
没遇到过用 slice 拷贝数组的写法吗?
特别是现在前端各种数据绑定设计,类似的拷贝用法应该很常见才对。 |