JavaScript进阶----《getter 和 setter 是什么》

前言: 这两个属性在学习前端的时候看到过,但是由于项目中没有用到过,所以一直没有细致的了解。今天 review 同事代码的时候,遇到了这个写法,看了半天也不知道如何处理。再不学习真的以后连别人的代码都不知道什么意思了。而后经过查阅 MDN 以后,颠覆了自己对 js 的基础知识 —“对象(object)” 的认知,并由此深感自己的不足。故今天来做一个简单总结,讲给同样在学习路上的你。

tips: 如果你是 react 开发,你可以选择直接跳跃至标题二开始阅读。如果你是 vue 开发,我强烈建议你从标题一开始阅读,你会更加有代入感的阅读本文。

因为作者主要是 vue 开发,本文的由来就是阅读同事的 vue 代码有感而作,但是也请 react 开发的同学不要害怕,没有标题一也并不会影响你阅读本文的整体感受🎁。

一. 初次相遇的场景

  1. 第一次遇到这两个名词的场景,是在学习 Vue3 教程的过程中,在看到 computed 的用法时,看到了下面这样一段描述:
    image.png
    原文链接:vue3 Computed 讲解

  2. 回顾一下我们在 vue3computed 常用的写法。我在项目中最常用的方法就是给 computed 传递一个回调函数, 这个回调函数返回值就是这个计算属性的值。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqFfVsVK-1688007173495)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/af7ff16637e04a37baaeeb0e63bbf7ec~tplv-k3u1fbpfcp-watermark.image?)]

  3. 随着写的项目越来越多,我逐渐形成了一个惯性思维,好像 computed 就是这样“仅此而已”。其实不然,它还有更进阶的用法,接下来让我们继续慢慢理解。

  4. 相信大家一定理解下面的代码为什么会报错。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-guD65dmV-1688007173495)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/79a9cbbd98494f77ada817b91c8f2d6f~tplv-k3u1fbpfcp-watermark.image?)]

  5. 因为我们上面是 computed 的基础用法,这样用其实你只给这个 name 这个属性设置了 getter。这样就导致了你这个 name 属性只能读取,而不能改写(或者叫做重新赋值)
    image.png

  6. 听到这里,你可能和最开始的我一样困惑。什么 getter? 我连单词 get 都没看见,你就在这里自言自语 getter,别急,一步一个脚印慢慢来。

  7. 仔细看我们上面的写法,我们如果只给 computed 函数一个函数作为参数。如下图:
    image.png
    那么其实上面的写法等价于下面的写法(tips:暂时忽略报错,这里的报错不是重点
    image.png

  8. 你可能更加好奇了,什么鬼,从哪里凭空冒出来一个 get? 为啥这样写就不能重新赋值了?在此之前你必须更加深入了解 object 这个类型。

二. object 属性的定义方法

  1. 这里我准备了一个空对象,现在我想让你给这个 obj 赋予一个叫做 name 的属性。值为字符串类型的 “韩振方”。你会怎么做?
    image.png

  2. 我觉得你甚至不需要思考,条件反射的都可以写出下面的代码。
    image.png

  3. 你要知道,其实这一步你是在完成一个对 obj属性描述过程

  4. 让我们完整的回顾上面的过程:你刚刚给 obj 这个对象添加了一个属性叫做 ‘name’,并且这个 name值(value) 是一个叫做 “韩振方” 的字符串。

  5. 接下来我将告诉你的是,在你 obj.name=“韩振方”的时候,你其实间接的调用了 Object 原型身上的 defineProperty 方法。

三. Object.defineProperty

  1. MDN 上查阅可知,这个函数有三个参数。
    image.png

  2. 让我换一种方法,重新写 obj.name=“韩振方” 这段代码,那么它其实等价于Object.defineProperty(obj,"name",{value:"韩振方",...})
    注意! 上面的代码不严谨,它省略了一部分内容。我只是想通过上面引出我们接下来要讲解十分重要的知识点属性描述符。 后面我会慢慢补充省略的内容。

  3. 由上面代码我们可以知道这个函数的基本用法,接收3个参数,第一个参数是要添加属性的对象(obj),第二个参数是要添加的属性名称(name),关键点是第三个属性,这个属性是一个对象类型的参数。我们的重点是搞清楚这第三个参数都有什么选项。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSJhbWcW-1688007173497)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c519fb5cfd24a80a09a0ab5af1366ee~tplv-k3u1fbpfcp-watermark.image?)]

  4. 由于篇幅限制,在本文中,我们暂时忽略“enumerable”和“configurable”这两个属性。我们重点看下面这几个属性。
    image.png

  5. 在讲解下面的知识之前,我想再强调一下,第三个参数属性描述符 是一个对象类型,{}它有且只有一些固定的键值对。它用来约束这个即将要定义的属性的一些行为。

四. value 和 writable

  1. value,我相信这个选项非常非常容易理解,就是你给这个属性即将赋予的值。

  2. 读懂了下面带红线的句子,你应该就明白了一个没有赋值的属性是为什么值是 undefined 的了吧?
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S06U9Aog-1688007173497)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/728e5e5c14974d90a42184912ec44cfe~tplv-k3u1fbpfcp-watermark.image?)]

    image.png

  3. writable 是否可写,这里可写说白了就是是否可以重新被赋值。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9FPaavn-1688007173497)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cc9beaa57e9480db0879b27df5d26eb~tplv-k3u1fbpfcp-watermark.image?)]

  4. 强调一下这里 writbale 默认值不是我们想象中的 true 而是 false

  5. 回顾我们上面的代码。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PX4rSwS7-1688007173497)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25909a2ff5094ba184b6eab34e917f2f~tplv-k3u1fbpfcp-watermark.image?)]
    由于我们没有定义 writable ,所以按道理来讲它是不可重新赋值的。 让我们验证一下:
    不出所料,控制台报错了,并且报错信息和猜测的一样,不能给只读属性赋值。
    image.png

  6. 让我们设置 writabletrue 再重新尝试一下。
    image.png
    可以看到控制台的错误没有了,并且正确输出了修改过后的值。
    image.png

  7. 别忘了,刚刚我们重新赋值的代码,obj.name="小韩" 这一步本质上还是在重复使用 Object.defineProperty 这个函数。正好也对应了 MDN 的这段解释。
    image.png

五. getter 和 setter

  1. 标题的内容终于到了,其实 gettersetter 并没有那么难理解。

  2. 首先让我们搞明白一个过程。下面的代码是在控制台输出 objname 属性的值。对吧?
    image.png

  3. 其实你的这个动作 obj.(注意有个点)obj点 的过程是在“读取obj 对象的 name 属性。注意这个 “读取” 的动作。这个读取其实就是对应了获取 value 的过程。
    image.png

  4. 这个动作正好就是 getter 要做的行为。首先别看叫 getter 就很害怕,它其实就是属性描述符的一个属性 get 而已,仅此而已。只不过这个属性的值是一个函数。起了个外国人名字,加了个 er 叫起来顺口而已。

  5. 什么?有点绕?还不懂?一个普通对象,有一个属性,属性值是一个函数。像下面,一个对象 hanzhenfang ,有一个属性叫做 skill,值是一个函数,能够被执行,执行后在控制台输出一个哈哈
    image.png
    怎么我这样写你就能明白,换个说法就不明白了呢?

  6. 回到 getter,我想你可能马上想到 getter 的用法应该像下面这样。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NnLAeyUT-1688007173498)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b48facfa1c704020888f8622e31d95fc~tplv-k3u1fbpfcp-watermark.image?)]
    对不起,这样是不允许的,因为 get 属性的返回值将会被作为属性值的读取结果给你。这样会造成编译器无法知道你的像 obj.name 这样的属性值读取过程该返回给你哪一个值。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtZvFggW-1688007173498)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/590b410b05d2440a95f0387fde9dd7e3~tplv-k3u1fbpfcp-watermark.image?)]

  7. 你只把 value 去掉也是不可以的,因为 writable 对应我们马上要讲的 setter
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZXYnpbHO-1688007173498)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c8b55c315c014a3abb4ce8a48eb678b7~tplv-k3u1fbpfcp-watermark.image?)]
    image.png

  8. 所以正确的 gettr 用法是下面这样。
    image.png
    image.png

  9. 让我们不设置 setter,尝试把 name 修改回 韩振方 试一下。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xhbSbM0I-1688007173499)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d831f02ba384432b1b93e63e2c4aaeb~tplv-k3u1fbpfcp-watermark.image?)]
    有了上面的 writable 的经验,我们大概率要翻车。果然控制台报错了,“你不能给一个只有 getter 的对象属性重新赋值。”
    image.png

  10. 聪明的你一定想到了下面的结论,没错, getter 对应的是 value ,而setter 对应的正是 writable

  11. setter 也是一个值为函数的属性,不过这个属性接收一个参数,这个参数正是赋值运算符右边的内容。(也就是等号右边的值)千万一定要仔细看我们下面的写法。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pq37cNnv-1688007173499)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b9d7bea116542aa8d17486fb0442e46~tplv-k3u1fbpfcp-watermark.image?)]

  12. 我们仅仅在 setter 函数的内部打印了一下新的值,而并没有对新的值做任何操作,那么其实我们 objname 属性仍为数字 10
    image.png
    验证一下:
    image.png

  13. 为什么要这样做呢?我举个简单的例子,这样会给我们一个十分重要的中间处理步骤。假设我在给 obj 重新命名。因为我姓,你改的名字里姓氏最起码得是才可以通过吧?你直接改成吴彦祖那不乱拉套了?
    image.png
    可以看到我们可以在 setter 正确拦截错误的操作。
    image.png

  14. 请原谅我啰嗦一大堆,因为我想如果像上面这样用实际例子演示可能会比 MDN 这样一段大白话更加通俗易懂。
    image.png

六. 重新分析 computed

回过头再看我们标题一的问题就显得十分清晰了。
image.png
image.png

七. 思考题

gettersetter 有一个经典的错误使用案例。请分析为什么下面的代码会引起递归导致栈溢出?
image.png
控制台输出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njikwhbo-1688007173500)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a3df9c48472420f89285f0d62b6c302~tplv-k3u1fbpfcp-watermark.image?)]
image.png
如果你明白了上面代码报错的原因,我想你也就明白了 gettersetter 🎁。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度智驾,与车路协同说「再见」

作者 | 魏启扬 来源 | 洞见新研社 在经历了裁员,全员停发年终奖之后,百度对智能交通事业部(ACE)的治理还在继续。 不久前,有媒体爆料称,百度已经将智能交通事业部(ACE)从原来的智能…

微信小程序——分页组件的创建与使用

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

C++类与对象(默认成员函数之拷贝构造函数)

接前几次的类与对象的默认函数的知识点,下来面是默认成员函数中的拷贝构造函数。是的,它的名字是拷贝构造函数,他其实也是一种构造函数,为什么呢?接下来你就知道了,我们直接看看代码,如下&#…

kafka入门,发送原理和生产者重要参数(三)

发送原理 在消息发送过程中,涉及两个线程,main线程和Sender线程。在main线程中创建了一个双端队列,RecordAccumulator,Sender过程不断从RecordAccumulator中拉取消息发送到Kafka Broker batch size:只有数据累计到batch.size之后&#xff0…

C#winform listBox组件批量删除

修改listBox组件属性:可以选中多个板坯号 选中板坯列表,在界面上点击删除按钮,触发删除方法deleteList: private void deleteList() { ListBox.SelectedIndexCollection sic listBoxProducts.SelectedIndice…

虹科分享|如何防范MOVEit transfer漏洞|高级威胁防御

美国网络安全和基础设施安全局(CISA)承认,它正在向几个联邦机构提供支持,这些机构在Progress(前身为IpSwitch)MOVEit传输解决方案中暴露出漏洞后被攻破。根据CISA发布的一份警报和网络安全公告,CL0P勒索软件团伙一直在积极利用漏洞进行数据外…

react中基于腾讯地图的地图选点,地址搜索逆向定位获取经纬度

react中基于腾讯地图的地图选点,地址搜索逆向定位获取经纬度 效果示例图地图组件tencentMap/index.jsx样式map.scss 使用案例 效果示例图 地图组件tencentMap/index.jsx import { useEffect, useRef, useState } from "react"; import "./map.scss&…

数据结构--单链表的插入删除

数据结构–单链表的插入&删除 目标 单链表的插入(位插、前插、后插) 单链表的删除 单链表的插入 按为序插入(带头结点) ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 思路:找到第i-1个结点,将新结点插入其…

SpringMVC

SpringMVC常用注解: 1:Controller:用于标记控制器类,表示该类是可以处理HTTP请求的。 2:RequestMapping:用于映射URL和处理方法。可以在类和方法上,类级别的RequestMapping会对其中所有的方法进行URL映射。参数支持Ant-style路径…

01.网络编程-基础概念

网络编程就是指编写互联网项目,项目可以通过网络传输数据进行通讯 网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的 1.1 软件结构…

掌握GDB调试工具,轻松排除bug!

一、什么是GDB gdb是GNU debugger的缩写,是编程调试工具。 GDB官网: https://www.gnu.org/software/gdb/GDB适用的编程语言: Ada / C / C / objective-c / Pascal 等。GDB的工作方式: 本地调试和远程调试。 目前release的最新版…

PG系列4:linux下编译安装PG15

文章目录 一. 源码安装1.1 下载并解压1.2 安装依赖包1.3 开始编译安装1.4 创建用户1.5 创建目录及修改权限1.6 设置环境变量1.7 初始化数据库1.8 启动和关闭数据库 二. 验证2.1 查看数据库后台进程2.2 验证和登陆数据库2.3 查看数据库版本2.4 查看数据库运行状态2.5 修改白名单…