【备忘】JS中Array的几个小坑

154 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

今天晚上改bug时碰见一个很奇怪的问题,也给大家分享一下。顺便总结一下我开发中常见的数组问题

map中嵌套异步操作

今天有一个小功能,是在三方接口里拉取一些数据,由于三方的图片设置了跨域,因此我通过原生的一个方法,把图片下载并转换为base64

伪代码基本如下,是一个vuex的actions:

async showPost({commit}){
    const res = await getPost()
    const list = res.map(e=>{
        imageToBase64(e.url)
        return {
         url:e.url,
         id:e.id,
         highResolution:'',
         lowResolution:require('../assets/images/default.png')
        }
    })
    commit('savePost',list)
}

lowResolution就是数据还未返回时的默认图,imageToBase64会在成功的时候更新对应highResolution。vue组件则大概是这样

<template>
<div>
    <img v-for="k in list" :key="k.id" :src="k.highResolution ?k.highResolution:k.lowResolution">
</div>
</template>
<script>
    export default {
     computed:{
        list(){
         return this.$store.state.postList
        }
     }
    }
</script>

打眼一看,并没有是什么问题,但是真机上却出现随机显示lowResolution的情况,大部分状态都是lowResolution的情况,着实让我愣了一下。

最后经过n次debug,我才发现问题所在。imageToBase64本质上是一个异步的方法,而且当你嵌套进map时,有可能第一次的结果返回了,但是map还没有结束,返回的base64就无法更新到state。也有可能imageToBase64运行的很慢,可能你map都结束了,值才返回,这时候就是正确的。因此需要这样处理,把异步放到map之外

async showPost({commit}){
    const res = await getPost()
    const list = res.map(e=>({
         url:e.url,
         id:e.id,
         highResolution:'',
         lowResolution:require('../assets/images/default.png')
    }))
    commit('savePost',list)
    state.postList.forEach(e=>{imageToBase64(e.url)})
}

确保值已经存入state后在去获取,这时候就不需要管imageToBase64什么时候返回了

循环中splice

这个应该属于一个思维误区,一般循环数组都是通过下表,如果此时你使用splice方法删除元素,数组的下标就会改变。

const arr = [1,2,3]
for(let i =0;i<arr.length;i++){
    if(arr[i]===2){
        arr.splice(i,1)
    }
}

解决办法很简单:使用filter

const arr = [1,2,3].filter(e=>e!==2)

unshift 的返回值

在数组的操作中,concat会返回数组,但是push、unshift这些都是只返回数组的长度,这样就容易造成记忆混乱,比如我就写过

//want [4,1,2,3]
arr = [1,2,3].unshift(4)
//result 
arr = 4

这个解决办法有很多,我常使用展开扩展符.

let arr = [1,2,3]
arr = [4,...arr]

filter内部是浅拷贝

这又是一个神奇的bug。看代码

// list = [{type:1},{type:2}]
const list = this.$store.state.list
    .filter(e=>e.type === 1)
    .map(e=>{
        e.type=2 
        return e
    })

本质上state.list是一个数组,内部元素都是对象。而我希望得到一个新数组。 运行完这段代码,你会发现state.list内部的值改变了。

**因为 filter本质上是浅拷贝,内部元素引用没有变

这里需要这样修改

const list = this.$store.state.list
    .filter(e=>e.type === 1)
    .map(e=>{
       
        return {...e,type:2}
    })

大家对于日常使用数组还发现有什么问题吗,欢迎留言讨论