js中map是否会影响元素组

先说结论:
当数组中的是普通类型,不会影响元素组,如果数组中的项是引用型类型,不一定会改变。

项目中遇到的问题

  1. 先说说最近在项目中(ps: 我们的项目是座屎山,经历了多人的手)遇到的问题:
    在现在模块化的项目中(我使用的redux + react),我在父组件中从redux状态管理器中拿到了images一个对象数组
images = [{
id: xx,
url:xx,
//等等
}]

然后从父组件中通过props传给了孙子组件,然后再孙子组件中通过修改其中某一项中的值,然后通过回到函数传回父组件中,再存入redux管理器中,父组件的回调函数是这样的:

   
   rejectCheckBoxCB = (data) => {//data是孙子组件中修改后的值
        let me = this;

       let images = me.props.auditContent.images;

            images.map(i => {
                if (i.id === data.id) {
                    i = data;
                }
                return i;
            });
            me.props.actions.updateAuditContent({images: images});//重新传入redux
    };

然后在之前的情况下没有任何问题,每次在孙子组件中修改后也能同步到redux

直到:

我在父组件的子组件中,也就是孙子组件和父组件的中间的一层做出了修改,我将父组件传入的images, 存入了本页面的state中(因为别的原因我要在本页面中操作images),然后在state中取得images,再作为props传给了孙子组件。。。。。,本来对于逻辑来说是没有任何问题,但是,就是因为我存了一下state,导致了线上的bug的产生。

解释原因:

接下来说一下原因:就是map中的一项被赋值,影不影响原来数组的问题:

let arr = [1, 2,3]
let arr_new =arr.map(item => {
item = item *2
return item
})
console.log(arr) //[1, 2, 3] 看出作为简单类型元素组是不会做出改变的
console.log(arr_new ) //[2, 4, 6]

那么什么情况下可以影响元素组呢?如果每一项的类型为引用型类型,并且直接修改他的属性值:

let arr  = [{a:1}, {a:2}]
let arr_new = arr.map(item => {
    item.a = 5
    return item
})
console.log(arr) //[{a:5}, {a:5}] 看出影响了元素组
console.log(arr_new ) //[{a:5}, {a:5}]

but 但是:

是不是只要是引用型类型都会改变呢?例如:


let arr  = [{a:1}, {a:2}]
let arr_new = arr.map(item => {
    item = {a:5, b:5}
    return item
})
console.log(arr) //[{a:1}, {a:2}] 并没有影响元素组
console.log(arr_new ) //[{a:5}, {a:5}]

这又是为啥???

因为在存入引用类型的时候存储变量的值是一个内存地址,如果你使用item.a去修改,顺着指针修改找到堆里面的存储的值,将数据改变之后,你读取arr直接读取修改后被在修改后地址的值

第一种:

初始状态:

栈(Stack)


arr -> Heap地址1

堆(Heap)


Heap地址1: [
    Heap地址2: { a: 1 },
    Heap地址3: { a: 2 }
]

图示:


Stack:     arr ---> Heap地址1 ----> [ Heap地址2 { a: 1 }, Heap地址3 { a: 2 } ]

  • 执行 map 方法

    • map 方法遍历数组中的每个对象,item 是对堆中对象的引用。
    • 修改 item.a = 5 时,直接操作堆中对象(Heap地址2Heap地址3),因此 arr 的内容会被修改。

    修改过程:

    • 遍历第一个元素(item 引用的是 Heap地址2),将其属性 a 改为 5。
    • 遍历第二个元素(item 引用的是 Heap地址3),将其属性 a 改为 5。

    修改后的内存如下:

    栈(Stack)

    
    arr -> Heap地址1
    arr_new2 -> Heap地址1
    
    

    堆(Heap)

    
    Heap地址1: [
        Heap地址2: { a: 5 },
        Heap地址3: { a: 5 }
    ]
    
    

    图示:

    
    Stack:     arr ---> Heap地址1 ----> [ Heap地址2 { a: 5 }, Heap地址3 { a: 5 } ]
               arr_new2 --也一样
    
    
  • 返回新数组 arr_new2

    • arr_new2 是一个新数组,但它的每个元素是对堆中原有对象的引用,因此与 arr 共享同一组对象。

第二种:

初始状态:

栈(Stack)


arr -> Heap地址1

堆(Heap)


Heap地址1: [
    Heap地址2: { a: 1 },
    Heap地址3: { a: 2 }
]

图示:


Stack:     arr ---> Heap地址1 ----> [ Heap地址2 { a: 1 }, Heap地址3 { a: 2 } ]

  1. 执行 map 方法

    • map 遍历 arr 中的每个元素。
    • 每次迭代,item 是对数组中某个对象的引用(例如 Heap地址2Heap地址3)。

    第一步:item = { a: 5, b: 5 }

    • item 是一个局部变量,它一开始引用堆中的原对象(例如 Heap地址2)。
    • 通过 item = { a: 5, b: 5 }重新赋值item,使其指向一个新的堆对象(例如 Heap地址4)。
    • 重新赋值只改变了 item 变量的引用,不会影响原数组中的对象。

    第二步:返回新对象

    • 新对象 { a: 5, b: 5 } 被返回并存储到新数组 arr_new 中。
    • 原数组 arr 不会被修改,因为原始引用仍然保留在 arr 中。

    修改后的内存如下:

    栈(Stack)

    
    arr -> Heap地址1
    arr_new -> Heap地址5
    
    

    堆(Heap)

    
    Heap地址1: [
        Heap地址2: { a: 1 },
        Heap地址3: { a: 2 }
    ]
    Heap地址5: [
        Heap地址4: { a: 5, b: 5 },
        Heap地址6: { a: 5, b: 5 }
    ]
    
    

    图示:

    
    Stack:     arr ---> Heap地址1 ----> [ Heap地址2 { a: 1 }, Heap地址3 { a: 2 } ]
               arr_new ---> Heap地址5 ----> [ Heap地址4 { a: 5, b: 5 }, Heap地址6 { a: 5, b: 5 } ]
    
    

为什么 arr 不会改变?

  1. 引用 vs 重新赋值
    • map 回调中,item 是对 arr 中对象的引用。
    • 通过 item = { a: 5, b: 5 },你只是让 item 指向一个新的对象,而没有修改原对象。
    • 因此,原数组 arr 中的对象保持不变。
  2. 局部作用域
    • item 是回调函数的局部变量,重新赋值不会影响外部数组。
    • arr 的元素仍然引用原来的对象。
posted @ 2024-12-20 22:00  heshanwan  阅读(17)  评论(0)    收藏  举报