对象和引用类型的赋值都是通过引用传递的方式进行的,这意味着变量实际上存储的是对象的引用,而不是对象本身的副本

这篇博客我主要想解释一下这句话:对象和引用类型的赋值都是通过引用传递的方式进行的,这意味着变量实际上存储的是对象的引用,而不是对象本身的副本。

其实这段话早在学习JS的时候就接触过,只是被我丢进了“记忆垃圾桶”,但是昨天遇到的一个bug,让死去的回忆突然攻击我

问题引出

起因是我在编写一个酒店住客管理的代码(纯前端项目,Pinia持久化存储)

首先我将Pinia仓库中的住客信息渲染在表格中

 

然后,利用element-ui组件库的表格组件渲染数据,并利用该组件的行数据对象scope.row获取一行的数据赋值给drawerMessage.value

目的是通过表格的“查看/编辑”按钮去打开一个右侧弹出框,并渲染对应的行数据

然而神奇的事情发生了,我将檀健次改成多多,对应的表格数据竟然也同步变化了,更神奇的是,Pinia仓库中的数据也改变了

 

为了便于大家理解,我把相关代码放在这

<script setup>
import { ref } from 'vue'

import { useHotelStore } from '@/stores'
const hotelStore = useHotelStore()

// 右侧弹出框
const drawer = ref(false)
const drawerMessage = ref()

const openDraw = scope => {
  drawer.value = true
  drawerMessage.value = scope
}

</script>

<template>
  <!-- 住客列表 -->
  <div class="bigContainer">

      <el-table :data="hotelStore.guestsList" height="100%" stripe style="width: 100%">
        <el-table-column label="编号" width="100">
          <template #default="scope">
            <span>{{ scope.$index + 1 }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="guestsName" label="姓名" width="180" />
        <el-table-column prop="guestsGender" label="性别" />
        <el-table-column prop="guestsId" label="身份证号" />
        <el-table-column prop="guestsPhone" label="手机号" />
        <el-table-column prop="guestsCheckIn" label="入住时间" />
        <el-table-column prop="guestsCheckOut" label="退房时间" />
        <el-table-column prop="guestsRoomType" label="房间类型" />
        <el-table-column prop="guestsRoomNumber" label="房间号" />
        <el-table-column label="操作">
          <template #default="scope">
            <el-button @click="openDraw(scope.row)" type="primary" drawer>查看/编辑</el-button>
          </template>
        </el-table-column>
      </el-table>


    <!-- 右侧弹出框 -->
    <el-drawer v-model="drawer" :direction="direction">
      <template #header>
        <h4>住客信息</h4>
      </template>
      <template #default>
        <div class="messageFlex">
          <div>
            <span class="drawerMessage">姓名</span>
            <el-input v-model=drawerMessage.guestsName style="width: 240px" placeholder='姓名' />
          </div>

          <div>
            <span class="drawerMessage">性别</span>
            <el-input v-model=drawerMessage.guestsGender style="width: 240px" placeholder='性别' />
          </div>

          <div>
            <span class="drawerMessage">身份证号</span>
            <el-input v-model=drawerMessage.guestsId style="width: 240px" placeholder='身份证号' />
          </div>

          <div>
            <span class="drawerMessage">手机号</span>
            <el-input v-model=drawerMessage.guestsPhone style="width: 240px" placeholder='手机号' />
          </div>

          <div>
            <span class="drawerMessage">入住时间</span>
            <el-input v-model=drawerMessage.guestsCheckIn style="width: 240px" placeholder='入住时间' />
          </div>

          <div>
            <span class="drawerMessage">退房时间</span>
            <el-input v-model=drawerMessage.guestsCheckOut style="width: 240px" placeholder='退房时间' />
          </div>

          <div>
            <span class="drawerMessage">房间类型</span>
            <el-input v-model=drawerMessage.guestsRoomType style="width: 240px" placeholder='房间类型' />
          </div>

          <div>
            <span class="drawerMessage">房间号</span>
            <el-input v-model=drawerMessage.guestsRoomNumber style="width: 240px" placeholder='房间号' />
          </div>
        </div>
      </template>
      <template #footer>
        <div style="flex: auto">
          <el-button @click="handleDelete">删除该住客</el-button>
          <el-button type="primary" @click="handleSave">保存修改</el-button>
        </div>
      </template>
    </el-drawer>

  </div>
</template>

明明是相互赋值的关系,为什么数据的变化却是绑定的呢?

接下来的内容由AI提供

问题解答

在你的代码中,drawerMessage 被设置为表格中某一行的数据引用。因此,当你打开抽屉并编辑输入框中的数据时,实际上是在直接修改该行的数据。由于 drawerMessage 引用了表格数据中的某一行,这些更改会自动反映在表格数据中。

以下是一些关键点,帮助解释为什么会发生这种情况:

数据引用

当你在 openDraw 方法中将 scope.row 赋值给 drawerMessage 时:

const openDraw = scope => {
  drawer.value = true
  drawerMessage.value = scope // 这里将当前行的引用赋值给 drawerMessage
}

drawerMessage 并不是一个新的对象,而是对 scope.row 所指向的对象的引用。因此,当你修改 drawerMessage 中的属性时,实际上是在修改 scope.row 的属性,即修改了 hotelStore.roomList 中对应行的数据

双向绑定

在模板中,你使用了 v-model 指令来绑定输入框与 drawerMessage 的属性:

<el-input v-model="drawerMessage.roomType" style="width: 240px" placeholder="房间类型" />
<el-input v-model="drawerMessage.roomNum" style="width: 240px" placeholder="房间号" />
<el-input v-model="drawerMessage.roomImg" style="width: 240px" placeholder="图片地址" />
<el-input v-model="drawerMessage.roomState" style="width: 240px" placeholder="状态" />

v-model 实现了双向绑定,当输入框的值改变时,相应的 drawerMessage 属性也会随之改变。而因为 drawerMessage 引用的是 scope.row,这意味着 scope.row 的对应属性也会被修改。

Pinia 数据更新

假设 hotelStore 使用 Pinia 来管理状态,当你修改表格数据中的某一行时,Pinia 的状态也会被更新。这是因为 Pinia 存储的数据是响应式的,任何变更都会自动传播到依赖这些数据的地方。

解决方案

如果你不希望修改 drawerMessage 时直接影响到 hotelStore 中的数据,可以克隆一份对象,以避免直接引用。你可以使用 Object.assign 或者 JSON.parse(JSON.stringify()) 来创建一个新对象:

const openDraw = scope => {
  drawer.value = true
  drawerMessage.value = { ...scope.row } // 使用扩展运算符克隆对象
}

这样,你在 drawerMessage 上的更改不会立即反映在 hotelStore 的数据上,直到你显式地保存更改为止。下面是一个简化的示例:

<el-button type="primary" @click="handleSave">保存修改</el-button>

handleSave 方法中,你可以将 drawerMessage 的数据同步回 hotelStore

const handleSave = () => {
  // 查找当前正在编辑的房间在 hotelStore.roomList 中的位置
  const index = hotelStore.roomList.findIndex(room => room.id === drawerMessage.value.id)
  
  // 如果找到了对应的房间(即 index 不为 -1)
  if (index !== -1) {
    // 用抽屉中的数据更新 hotelStore.roomList 中的对应房间的数据
    hotelStore.roomList[index] = { ...drawerMessage.value }
  }
  
  // 关闭抽屉
  drawer.value = false
}

这种方式确保只有在用户点击“保存修改”按钮时,才会将更改应用到 hotelStore 中的数据,从而避免了实时同步带来的问题。

原理解释

所以什么数据类型会出现上述情况?

在 JavaScript 中,主要是对象(包括数组、函数等)以及其他引用类型会出现上述情况。这是因为在 JavaScript 中,对象和引用类型的赋值都是通过引用传递的方式进行的,这意味着变量实际上存储的是对象的引用,而不是对象本身的副本。

除了对象和引用类型之外,基本数据类型(如数字、字符串、布尔值、null 和 undefined)在赋值时会创建副本,并且对副本的更改不会影响原始值,因为它们在内存中是独立存储的。这种赋值方式被称为值传递。

因此,只有对象和引用类型会出现上述描述的情况,而基本数据类型则不会。

代码示例1:对象赋值

const obj1 = { a: 1, b: 2 };
const obj2 = obj1;

obj1.c = 3;

console.log(obj2); // { a: 1, b: 2, c: 3 }

代码示例2:数组赋值

const array1 = [1, 2, 3];
const array2 = array1;

array1.push(4);

console.log(array2); // [1, 2, 3, 4]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/701317.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++进阶】模板与仿函数:C++编程中的泛型与函数式编程思想

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;栈和队列相关知识 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀模板进阶 &#x1f9e9;<&…

Javascript学习之路:js中关于遍历总结

循环/遍历 循环&#xff0c;就是一遍又一遍的重复执行相同或者相似的代码循环结构的两个要素 循环体–要执行的相同或相似的语句循环条件–重复执行的次数&#xff0c;或者继续执行循环的条件 &#x1f449;while循环 while循环语法格式 while(boolean表达式){循环体语句}//…

数据库选型实践:如何避开分库分表痛点 | OceanBase用户实践

随着企业业务的不断发展&#xff0c;数据量往往呈现出快速的增长趋势。使用MySQL的用户面对这种增长&#xff0c;普遍选择采用分库分表技术作为应对方案。然而&#xff0c;这一方案常在后期会遇到很多痛点。 分库分表的痛点 痛点 1&#xff1a;难以保证数据一致性。由于分库分…

算法之分治

分而治之 分治法所能解决的问题一般具有以下几个特征&#xff1a; 1) 该问题的规模缩小到一定的程度就可以容易地解决 2) 该问题可以分解为若干个规模较小的子问题&#xff0c;即该问题具有最优子结构性质 3) 利用该问题分解出的子问题的解可以合并为该问题的解 4) 该问题所分…

Redis在互联网大厂中的应用案例分析

携程金融的Redis架构 携程金融在经过多年的演进后,形成了多层次的系统架构,其中基础数据(如用户信息、产品信息、订单信息等)由底层系统产生,并服务于所有的金融系统。这些基础数据通过统一的缓存服务(系统名utag)进行缓存。缓存数据具有全量、准实时、永久有效的特点,…

【SpringBoot + Vue 尚庭公寓实战】标签和配套管理接口实现接口实现(六)

【SpringBoot Vue 尚庭公寓实战】标签和配套管理接口实现接口实现&#xff08;六&#xff09; 文章目录 【SpringBoot Vue 尚庭公寓实战】标签和配套管理接口实现接口实现&#xff08;六&#xff09;1、保存或更新标签信息2、根据id删除标签信息3、根据类型查询配套列表4、新…

【机器学习】基于CNN-RNN模型的验证码图片识别

1. 引言 1.1. OCR技术研究的背景 1.1.1. OCR技术能够提升互联网体验 随着互联网应用的广泛普及&#xff0c;用户在日常操作中频繁遇到需要输入验证码的场景&#xff0c;无论是在登录、注册、支付还是其他敏感操作中&#xff0c;验证码都扮演着重要角色来确保安全性。然而&am…

OCC+VS+QT项目配置

1、安装好VS2022、QT、OCC安装包后&#xff0c;在VS2022中创建一个项目&#xff0c;具体如下&#xff1a; 2、配置步骤1中创建的项目的属性&#xff0c;具体如下&#xff1a; VC目录>>>包含目录&#xff1a; 添加文件目录如上三个。 VC目录>>>库目录&#xf…

【权威发布】2024年人工智能与机械设计国际会议(ICAIMD 2024)

2024年人工智能与机械设计国际会议 2024 International Conference on Artificial Intelligence and Mechanical Design 会议简介 2024年人工智能与机械设计国际会议是一个专注于探讨人工智能与机械设计交叉领域最新发展的国际盛会。本次会议汇集了全球顶尖的专家学者、研究人员…

【Apollo配置中心】集成springboot自动监听属性变更和动态发布配置

1. 背景 在实际项目中&#xff0c;Spring Boot项目结合使用Apollo配置中心时&#xff0c;经常会遇到需要更新Apollo上的项目的一些配置&#xff0c;比如测试环境或生产环境中&#xff0c;需要修改某个类的属性值&#xff0c;如果我们在Apollo上更新了配置&#xff0c;已经在运…

Swift 序列(Sequence)排序面面俱到 - 从过去到现在(三)

概述 在上一篇 Swift 序列(Sequence)排序面面俱到 - 从过去到现在(二) 博文中,我们介绍了如何构建一个自定义类型中“多属性”排序的通用实现。 而在本课中我们将再接再厉介绍 iOS 15+ 中新的排序机制,并简要剖析就地排序(In-place sorting)对运行性能有着怎样的显著影…

Stable Diffusion 如何写出更优雅的 Prompt

在看了前面的课程后&#xff0c; 相信很多人都会有一个困惑&#xff0c;这个 prompt 咋写… 为什么我写的时候只能憋出来了一个 a girl, a boy, beautify … 再也想不到其他的了&#xff0c; 总感觉是吃了没文化的亏&#xff1f; 这一节课我们就来讲一讲 如何写好 prompt …

docker 部署kafka,zokeeper,快速开发模式

docker-compse 启动文件 version: 3name: kafka1services:zookeeper:image: bitnami/zookeeper:latestports:- "2181:2181"environment:- ALLOW_ANONYMOUS_LOGINyes # volumes: # - zookeeper_vol:/data # - zookeeper_vol:/datalog # - zookeepe…

MATLAB神经网络---激活层

reluLayer 修正线性单元 (ReLU) 层 ReLU 层对输入的每个元素执行阈值运算&#xff0c;其中任何小于零的值都设置为零。 此运算等效于 语法 layer reluLayer 创建一个 ReLU 层。 layer reluLayer(Name,Name) 创建一个 ReLU 层&#xff0c;并使用名称-值对组设置可选的 Nam…

光纤跳线(又称光纤连接器)的种类

光纤跳线&#xff08;又称光纤连接器&#xff09;&#xff0c;也就是接入光模块的光纤接头&#xff0c;也有好多种&#xff0c;且相互之间不可以互用。SFP模块接LC光纤连接器&#xff0c;而GBIC接的是SC光纤连接器。下面对网络工程中几种常用的光纤连接器进行详细的说明&#x…

4月美团社招测试凉经

面经哥只做互联网社招面试经验分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 一面&#xff08;凉凉&#xff09; 1、自我介绍 2、移动端的需求怎么测试&#xff1f; 3、移动端的测试侧重点是什么&#xff1f; 4、自动化测试…

如何设计一个秒杀系统?

如何从整体角度&#xff0c;去设计一个秒杀系统。秒杀系统主要有这几个特征&#xff1a; 瞬时间的流量特别高。过了秒杀的时间&#xff0c;流量就会瞬时结束 大批量用户同时请求极少数商品 在秒杀时间前&#xff0c;可能会有很多请求过来。比如在11点抢票开始&#xff0c;10点…

后继者00

题目链接 后继者 题目描述 注意点 题目中的树是二叉搜索树节点p在二叉搜索树中一定存在 解答思路 本题关键是找到值大于节点p的值的第一个节点&#xff0c;因为本题中的树是二叉搜索树&#xff0c;所以左子树的值始终小于根节点&#xff0c;右子树的值始终大于根节点访问到…

导入导出带下拉框模版(EasyExcel)

前言 项目进行到新的一个迭代了&#xff0c;赶了1周需求&#xff0c;接口终于处理完了。分享记录下迭代中处理导入、导出、下载模版功能的细节吧。 一、场景 EasyExcel&#xff08;阿里&#xff09;实现Excel数据处理三层表头&#xff0c;第二、三层表头动态数据根据第二、三层…

【技术】MySQL 8.4 免安装版配置

MySQL 8.4 免安装版配置 官网下载压缩包解压文件创建配置文件初始化数据库安装MySQL服务链接数据库修改密码 官网下载压缩包 从MySQL官网下载压缩包&#xff0c;官网&#xff1a;https://www.mysql.com/ 头部菜单点击【DOWNLOADS】&#xff0c;跳转到下载页面。在页面底部点击…