关于前端:JavaScript进阶getter-和-setter-是什么

50次阅读

共计 3165 个字符,预计需要花费 8 分钟才能阅读完成。

前言: 这两个属性在学习前端的时候看到过,然而因为我的项目中没有用到过,所以始终没有粗疏的理解。明天 review 共事代码的时候,遇到了这个写法,看了半天也不晓得如何解决。再不学习真的当前连他人的代码都不晓得什么意思了。而后通过查阅 MDN 当前,颠覆了本人对 js 的基础知识 —“对象(object)”的认知,并由此深感本人的有余。故明天来做一个简略总结,讲给同样在学习路上的你。

tips: 如果你是 react 开发,你能够抉择间接跳跃至 题目二 开始浏览。如果你是 vue 开发,我强烈建议你从 题目一 开始浏览,你会更加有代入感的浏览本文。

因为作者次要是 vue 开发,本文的由来就是浏览共事的 vue 代码有感而作,然而也请 react 开发的同学不要胆怯,没有 题目一 也并不会影响你浏览本文的整体感触🎁。

一. 首次相遇的场景

  1. 第一次遇到这两个名词的场景,是在学习 Vue3 教程的过程中,在看到 computed 的用法时,看到了上面这样一段形容:

    原文链接:vue3 Computed 解说

  2. 回顾一下咱们在 vue3computed 罕用的写法。我在我的项目中最罕用的办法就是给 computed 传递一个回调函数,这个回调函数返回值就是这个计算属性的值。
  3. 随着写的我的项目越来越多,我逐步造成了一个惯性思维,如同 computed 就是这样“仅此而已”。其实不然,它还有更进阶的用法,接下来让咱们持续缓缓了解。
  4. 置信大家肯定了解上面的代码为什么会报错。
  5. 因为咱们下面是 computed 的根底用法,这样用其实你只给这个 name 这个属性设置了 getter。这样就导致了你这个 name 属性只能 读取 ,而不能 改写(或者叫做从新赋值)
  6. 听到这里,你可能和最开始的我一样困惑。什么 getter? 我连单词 get 都没看见,你就在这里喃喃自语 getter,别急,一步一个脚印慢慢来。
  7. 认真看咱们下面的写法,咱们如果只给 computed 函数一个函数作为参数。如下图:

    那么其实下面的写法等价于上面的写法(tips:临时疏忽报错,这里的报错不是重点

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

二. object 属性的定义方法

  1. 这里我筹备了一个空对象,当初我想让你给这个 obj 赋予一个叫做 name 的属性。值为字符串类型的 “韩振方”。你会怎么做?
  2. 我感觉你甚至不须要思考,条件反射的都能够写出上面的代码。
  3. 你要晓得,其实这一步你是在实现一个对 obj 属性形容过程
  4. 让咱们残缺的回顾下面的过程:你刚刚给 obj 这个对象增加了一个属性叫做 ‘name’,并且这个 name值 (value) 是一个叫做“ 韩振方”的字符串。
  5. 接下来我将通知你的是,在你 obj.name=“韩振方”的时候,你其实间接的调用了 Object 原型身上的 defineProperty 办法。

三. Object.defineProperty

  1. MDN 上查阅可知,这个函数有三个参数。
  2. 让我换一种办法,从新写 obj.name=“韩振方” 这段代码,那么它其实等价于 Object.defineProperty(obj,"name",{value:"韩振方",...})
    留神! 下面的代码不谨严,它省略了一部分内容。我只是想通过下面引出咱们接下来要解说非常重要的知识点 属性描述符。 前面我会缓缓补充省略的内容。
  3. 由下面代码咱们能够晓得这个函数的根本用法,接管 3 个参数,第一个参数是要增加属性的对象 (obj), 第二个参数是要增加的属性名称(name),关键点是第三个属性,这个属性是一个 对象 类型的参数。咱们的重点是搞清楚这第三个参数都有什么选项。
  4. 因为篇幅限度,在本文中,咱们临时疏忽“enumerable”和“configurable”这两个属性。咱们重点看上面这几个属性。
  5. 在解说上面的常识之前,我想再强调一下,第三个参数 属性描述符 是一个对象类型,{} 它有且只有一些固定的 键值对 。它用来 束缚 这个行将要定义的属性的一些行为。

四. value 和 writable

  1. value,我置信这个选项十分非常容易了解,就是你给这个属性行将赋予的值。
  2. 读懂了上面带红线的句子,你应该就明确了一个没有赋值的属性是为什么值是 undefined 的了吧?

  3. writable 是否可写 ,这里 可写 说白了就是 是否能够从新被赋值。
  4. 强调一下这里 writbale 默认值不是咱们设想中的 true 而是 false
  5. 回顾咱们下面的代码。

    因为咱们没有定义 writable,所以按情理来讲它是 不可从新赋值的。 让咱们验证一下:
    不出所料,控制台报错了,并且报错信息和猜想的一样,不能给只读属性赋值。

  6. 让咱们设置 writabletrue 再从新尝试一下。

    能够看到控制台的谬误没有了,并且正确输入了批改过后的值。

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

五. getter 和 setter

  1. 题目的内容终于到了,其实 gettersetter 并没有那么难了解。
  2. 首先让咱们搞明确一个过程。上面的代码是在控制台输入 objname 属性的值。对吧?
  3. 其实你的这个动作 obj.(留神有个点)obj 点 的过程是在“ 读取obj 对象的 name 属性。留神这个 “读取” 的动作。这个读取其实就是对应了获取 value 的过程。
  4. 这个动作正好就是 getter 要做的行为。首先别看叫 getter 就很胆怯,它其实就是 属性描述符 的一个属性 get 而已,仅此而已。只不过这个属性的值是一个函数。起了个外国人名字,加了个 er 叫起来顺口而已。
  5. 什么?有点绕?还不懂?一个一般对象,有一个属性,属性值是一个函数。像上面,一个对象 hanzhenfang,有一个属性叫做 skill,值是一个函数,可能被执行,执行后在控制台输入一个 哈哈

    怎么我这样写你就能明确,换个说法就不明确了呢?

  6. 回到 getter,我想你可能马上想到 getter 的用法应该像上面这样。

    对不起,这样是不容许的,因为 get 属性的返回值将会被作为属性值的读取后果给你。这样会造成编译器无奈晓得你的像 obj.name 这样的属性值 读取 过程该返回给你哪一个值。

  7. 你只把 value 去掉也是不能够的,因为 writable 对应咱们马上要讲的 setter
  8. 所以正确的 gettr 用法是上面这样。
  1. 让咱们不设置 setter,尝试把 name 批改回 韩振方 试一下。

    有了下面的 writable 的教训,咱们大概率要翻车。果然控制台报错了,“你不能给一个只有 getter 的对象属性从新赋值。”

  2. 聪慧的你肯定想到了上面的论断,没错,getter 对应的是 value,而setter 对应的正是 writable
  3. setter 也是一个值为函数的属性,不过这个属性接管一个参数,这个参数正是 赋值运算符 左边的内容。(也就是等号左边的值)千万肯定 要认真看咱们上面的写法。
  4. 咱们仅仅在 setter 函数的外部 打印了 一下新的值,而并没有对新的值做任何操作,那么其实咱们 objname 属性仍为数字 10

    验证一下:

  5. 为什么要这样做呢?我举个简略的例子,这样会给咱们一个非常重要的 两头解决步骤 。假如我在给 obj 重新命名。因为我姓,你改的名字里姓氏最起码得是 才能够通过吧?你间接改成 吴彦祖 那不乱拉套了?

    能够看到咱们能够在 setter 正确拦挡谬误的操作。

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

六. 从新剖析 computed

回过头再看咱们题目一的问题就显得非常清晰了。

七. 思考题

gettersetter 有一个经典的谬误应用案例。请剖析为什么上面的代码会引起递归导致栈溢出?

控制台输入

如果你明确了下面代码报错的起因,我想你也就明确了 gettersetter 🎁。

正文完
 0