共计 3165 个字符,预计需要花费 8 分钟才能阅读完成。
前言: 这两个属性在学习前端的时候看到过,然而因为我的项目中没有用到过,所以始终没有粗疏的理解。明天 review 共事代码的时候,遇到了这个写法,看了半天也不晓得如何解决。再不学习真的当前连他人的代码都不晓得什么意思了。而后通过查阅 MDN 当前,颠覆了本人对 js 的基础知识 —“对象(object)”的认知,并由此深感本人的有余。故明天来做一个简略总结,讲给同样在学习路上的你。
tips: 如果你是 react 开发,你能够抉择间接跳跃至 题目二 开始浏览。如果你是 vue 开发,我强烈建议你从 题目一 开始浏览,你会更加有代入感的浏览本文。
因为作者次要是 vue 开发,本文的由来就是浏览共事的 vue 代码有感而作,然而也请 react 开发的同学不要胆怯,没有 题目一 也并不会影响你浏览本文的整体感触🎁。
一. 首次相遇的场景
- 第一次遇到这两个名词的场景,是在学习 Vue3 教程的过程中,在看到 computed 的用法时,看到了上面这样一段形容:
原文链接:vue3 Computed 解说
- 回顾一下咱们在 vue3 中 computed 罕用的写法。我在我的项目中最罕用的办法就是给 computed 传递一个回调函数,这个回调函数返回值就是这个计算属性的值。
- 随着写的我的项目越来越多,我逐步造成了一个惯性思维,如同 computed 就是这样“仅此而已”。其实不然,它还有更进阶的用法,接下来让咱们持续缓缓了解。
- 置信大家肯定了解上面的代码为什么会报错。
- 因为咱们下面是 computed 的根底用法,这样用其实你只给这个 name 这个属性设置了 getter。这样就导致了你这个 name 属性只能 读取 ,而不能 改写(或者叫做从新赋值)。
- 听到这里,你可能和最开始的我一样困惑。什么 getter? 我连单词 get 都没看见,你就在这里喃喃自语 getter,别急,一步一个脚印慢慢来。
- 认真看咱们下面的写法,咱们如果只给 computed 函数一个函数作为参数。如下图:
那么其实下面的写法等价于上面的写法(tips:临时疏忽报错,这里的报错不是重点)
- 你可能更加好奇了,什么鬼,从哪里凭空冒出来一个 get? 为啥这样写就不能从新 赋值 了?在此之前你必须更加深刻理解 object 这个类型。
二. object 属性的定义方法
- 这里我筹备了一个空对象,当初我想让你给这个 obj 赋予一个叫做 name 的属性。值为字符串类型的 “韩振方”。你会怎么做?
- 我感觉你甚至不须要思考,条件反射的都能够写出上面的代码。
- 你要晓得,其实这一步你是在实现一个对 obj 的 属性形容过程。
- 让咱们残缺的回顾下面的过程:你刚刚给 obj 这个对象增加了一个属性叫做 ‘name’,并且这个 name 的 值 (value) 是一个叫做“ 韩振方”的字符串。
- 接下来我将通知你的是,在你
obj.name=“韩振方”
的时候,你其实间接的调用了 Object 原型身上的 defineProperty 办法。
三. Object.defineProperty
- 从 MDN 上查阅可知,这个函数有三个参数。
- 让我换一种办法,从新写
obj.name=“韩振方”
这段代码,那么它其实等价于Object.defineProperty(obj,"name",{value:"韩振方",...})
留神! 下面的代码不谨严,它省略了一部分内容。我只是想通过下面引出咱们接下来要解说非常重要的知识点 属性描述符。 前面我会缓缓补充省略的内容。 - 由下面代码咱们能够晓得这个函数的根本用法,接管 3 个参数,第一个参数是要增加属性的对象 (obj), 第二个参数是要增加的属性名称(name),关键点是第三个属性,这个属性是一个 对象 类型的参数。咱们的重点是搞清楚这第三个参数都有什么选项。
- 因为篇幅限度,在本文中,咱们临时疏忽“enumerable”和“configurable”这两个属性。咱们重点看上面这几个属性。
- 在解说上面的常识之前,我想再强调一下,第三个参数 属性描述符 是一个对象类型,
{}
它有且只有一些固定的 键值对 。它用来 束缚 这个行将要定义的属性的一些行为。
四. value 和 writable
- value,我置信这个选项十分非常容易了解,就是你给这个属性行将赋予的值。
- 读懂了上面带红线的句子,你应该就明确了一个没有赋值的属性是为什么值是
undefined
的了吧? - writable 是否可写 ,这里 可写 说白了就是 是否能够从新被赋值。
- 强调一下这里 writbale 默认值不是咱们设想中的 true 而是 false。
- 回顾咱们下面的代码。
因为咱们没有定义 writable,所以按情理来讲它是 不可从新赋值的。 让咱们验证一下:
不出所料,控制台报错了,并且报错信息和猜想的一样,不能给只读属性赋值。 - 让咱们设置 writable 为 true 再从新尝试一下。
能够看到控制台的谬误没有了,并且正确输入了批改过后的值。
- 别忘了,刚刚咱们从新赋值的代码,
obj.name="小韩"
这一步实质上还是在重复使用 Object.defineProperty 这个函数。正好也对应了 MDN 的这段解释。
五. getter 和 setter
- 题目的内容终于到了,其实 getter 和 setter 并没有那么难了解。
- 首先让咱们搞明确一个过程。上面的代码是在控制台输入 obj 的 name 属性的值。对吧?
- 其实你的这个动作 obj.(留神有个点),obj 点 的过程是在“ 读取”obj 对象的 name 属性。留神这个 “读取” 的动作。这个读取其实就是对应了获取 value 的过程。
- 这个动作正好就是 getter 要做的行为。首先别看叫 getter 就很胆怯,它其实就是 属性描述符 的一个属性 get 而已,仅此而已。只不过这个属性的值是一个函数。起了个外国人名字,加了个 er 叫起来顺口而已。
- 什么?有点绕?还不懂?一个一般对象,有一个属性,属性值是一个函数。像上面,一个对象 hanzhenfang,有一个属性叫做 skill,值是一个函数,可能被执行,执行后在控制台输入一个 哈哈。
怎么我这样写你就能明确,换个说法就不明确了呢?
- 回到 getter,我想你可能马上想到 getter 的用法应该像上面这样。
对不起,这样是不容许的,因为 get 属性的返回值将会被作为属性值的读取后果给你。这样会造成编译器无奈晓得你的像
obj.name
这样的属性值 读取 过程该返回给你哪一个值。 - 你只把 value 去掉也是不能够的,因为 writable 对应咱们马上要讲的 setter。
- 所以正确的 gettr 用法是上面这样。
- 让咱们不设置 setter,尝试把 name 批改回 韩振方 试一下。
有了下面的 writable 的教训,咱们大概率要翻车。果然控制台报错了,“你不能给一个只有 getter 的对象属性从新赋值。”
- 聪慧的你肯定想到了上面的论断,没错,getter 对应的是 value,而setter 对应的正是 writable。
- setter 也是一个值为函数的属性,不过这个属性接管一个参数,这个参数正是 赋值运算符 左边的内容。(也就是等号左边的值)千万肯定 要认真看咱们上面的写法。
- 咱们仅仅在 setter 函数的外部 打印了 一下新的值,而并没有对新的值做任何操作,那么其实咱们 obj 的 name 属性仍为数字 10。
验证一下:
- 为什么要这样做呢?我举个简略的例子,这样会给咱们一个非常重要的 两头解决步骤 。假如我在给 obj 重新命名。因为我姓 韩,你改的名字里姓氏最起码得是 韩才能够通过吧?你间接改成 吴彦祖 那不乱拉套了?
能够看到咱们能够在 setter 正确拦挡谬误的操作。
- 请原谅我啰嗦一大堆,因为我想如果像下面这样用理论例子演示可能会比 MDN 这样一段大白话更加通俗易懂。
六. 从新剖析 computed
回过头再看咱们题目一的问题就显得非常清晰了。
七. 思考题
getter 和 setter 有一个经典的谬误应用案例。请剖析为什么上面的代码会引起递归导致栈溢出?
控制台输入
如果你明确了下面代码报错的起因,我想你也就明确了 getter 和 setter 🎁。