在工作的过程中踩了很多的由浅拷贝导致的坑,今天总结在这里,希望对大家有所帮助
1. 组件中直接抛出一个引用类型变量
🌰举个例子 (ps: 以下代码为伪代码,主要展示逻辑用)
子组件(uploadImg)定义一个上传图片的组件
基础功能:a. 上传图片,上传成功后抛出change 事件。
b. 上传成功的图片平铺展示,并展示删除按钮。点击删除按钮,弹出二次确认框。点击确认则删除,点击取消则不删除。
  
   - 
    
     
    
    
     
      // uploadImg.vue
     
    
- 
    
     
    
    
     
      // 上传图片组件——子组件 
     
    
- 
    
     
    
    
     
      <template>
     
    
- 
    
     
    
    
        
      <upload @change="uploadSuccess">
      </upload>
     
    
- 
    
     
    
    
        
      <div>
     
    
- 
    
     
    
    
         
      <div v-for="(item, fileIndex) in imgUrls" :key="fileIndex" class="file-content">
     
    
- 
    
     
    
    
               
      <div class="file-item">
     
    
- 
    
     
    
    
                 
      <div @click="delFile(fileIndex)" class="file-close">
     
    
- 
    
     
    
    
                   
      <el-tooltip class="item" effect="dark" content="删除" placement="right-start">
     
    
- 
    
     
    
    
                     
      <el-icon class="el-icon-close">
      </el-icon>
     
    
- 
    
     
    
    
                   
      </el-tooltip>
     
    
- 
    
     
    
    
                 
      </div>
     
    
- 
    
     
    
    
                 
      </div>
     
    
- 
    
     
    
    
               
      </div>
     
    
- 
    
     
    
    
             
      </div>
     
    
- 
    
     
    
    
     
      </div>
     
    
- 
    
     
    
    
     
      </template>
     
    
- 
    
     
    
    
     
      <script>
      
     
    
- 
    
     
    
    
     
      data(
      ){
     
    
- 
    
     
    
    
         
      return {
     
    
- 
    
     
    
    
             
      imgUrls: []
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      },
     
    
- 
    
     
    
    
     
      methods: {
     
    
- 
    
     
    
    
         
      // 上传成功
     
    
- 
    
     
    
    
     
          uploadSuccess (files) {
     
    
- 
    
     
    
    
     
            files.
      forEach(
      item => {
     
    
- 
    
     
    
    
             
      this.
      imgUrls.
      push({
     
    
- 
    
     
    
    
     
                ...item,
     
    
- 
    
     
    
    
               
      fileName: item.
      fileName
     
    
- 
    
     
    
    
     
              })
     
    
- 
    
     
    
    
     
            })
     
    
- 
    
     
    
    
           
      this.$emit(
      'change', 
      this.
      imgUrls)
     
    
- 
    
     
    
    
     
          },
     
    
- 
    
     
    
    
         
      // 删除文件
     
    
- 
    
     
    
    
     
          delFile (fileIndex) {
     
    
- 
    
     
    
    
           
      this.$confirm(
      '图片删除后不能恢复,确认删除?', 
      '提示').
      then(
      () => {
     
    
- 
    
     
    
    
             
      this.
      imgUrls.
      splice(fileIndex, 
      1)
     
    
- 
    
     
    
    
     
            })
     
    
- 
    
     
    
    
           
      this.$emit(
      'change', 
      this.
      imgUrls)  
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      </script>
     
    
 父组件使用方法1
  
   - 
    
     
    
    
     
      <template>
     
    
- 
    
     
    
    
         
      <upload-img @change="change">
      </upload-img>
     
    
- 
    
     
    
    
     
      </template>
     
    
- 
    
     
    
    
     
      <script>
      
     
    
- 
    
     
    
    
         
      data(
      ){
     
    
- 
    
     
    
    
             
      return {
     
    
- 
    
     
    
    
     
                  imageList
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          },
     
    
- 
    
     
    
    
         
      methods: {
     
    
- 
    
     
    
    
             
      change(
      list) {
     
    
- 
    
     
    
    
                   
      this.
      imageList = list
     
    
- 
    
     
    
    
                   
      console.
      log(
      this.
      imageList)
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      </script>
     
    
 我们使用以上方法,点击删除图片,确认删除。
获取到的 this.imageList,是删除成功之后的图片数组,无异常。删除和上传给父组件的值都是正确的。
父组件使用方法2
  
   - 
    
     
    
    
     
      <template>
     
    
- 
    
     
    
    
         
      <upload-img @change="change">
      </upload-img>
     
    
- 
    
     
    
    
     
      </template>
     
    
- 
    
     
    
    
     
      <script>
      
     
    
- 
    
     
    
    
         
      data(
      ){
     
    
- 
    
     
    
    
             
      return {
     
    
- 
    
     
    
    
     
                  imageList
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          },
     
    
- 
    
     
    
    
         
      methods: {
     
    
- 
    
     
    
    
             
      change(
      list) {
     
    
- 
    
     
    
    
                   
      this.
      imageList = list.
      map(
      item => item.
      url)
     
    
- 
    
     
    
    
                   
      console.
      log(
      this.
      imageList)
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      </script>
     
    
 我们使用以上方法,点击删除图片,确认删除。
获取到的 this.imageList,是删除之前的图片数组,有异常。
为啥会产生这样的异常呐??我们来分析一下

由上图可见,抛出change事件的时机,和弹出二次确认弹窗的时间是异步的。
即:在弹出二次确认弹窗的时候,chang事件就已经触发了。反而在点击二次确认弹窗的确定按钮的时候并未触发chang事件。
那么问题来了,为啥第一种方式无异常咧?因为我们change事件抛出的是一个引用类型的数据,并且父组件对该值进行了浅拷贝。等子组件的imgUrls值更新之后,父组件的imageList的值也进行了更新。
第二种方式由异常的原因:第二种方法在赋值之前使用了一个map 的方法,map 会返回一个新数组,即进行了深拷贝。
2. 直接将引用类型赋值给一个新的变量,对新变量进行处理
🌰举个例子
form表单中,输入一些数据,调用后端接口传参数是,需要对数据进行一些处理,比如金额,需要由元转成分
这时直接进行转化,就是导致页面上的展示数据,也会进行相应的变动。
常见于:数据长度变多或者变少,对象修改了某一个key 的值,却无变化。
常见的浅拷贝的方法?
1. Object.assign()
2. 解构赋值
3. ‘=’赋值
常见解决方法
1. 手写深拷贝递归复制
2. JSON做字符串转换
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1)) 
该方法有副作用
a. 它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
b.RegExp对象是无法通过这种方式深拷贝
c.function没办法转成JSON
3. 使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
附上创建对象的一些方法的差异:
附上创建对象的一些方法的差异:Object.create()、new Object()和{}的区别 - 掘金
4. 三方函数(常用)
如lodash
转载:https://blog.csdn.net/weixin_39975379/article/details/128715962
 
					