面试官:你了解属性描述符吗?

宝宝.jpg
面试官:你了解属性描述符吗?

我:额(⊙o⊙)… , 恨不得把自己的脑子,打开看看怎么这么空白啊。

面试官:你平常没怎么学习过框架的源码吧,建议可以多看看。

心里想着,凉了下一家吧,他人还怪好的咧,又遇到一个新知识盲区。

什么是属性描述符?

前端宝宝解释:字面意思很好理解,描述一个属性的符号。

正规军解释 : 属性描述符是用于描述对象的属性特性以及其行为的一种机制。属性描述符包括数据描述符存取描述符两种。

  1. 数据描述符: 数据描述符用于描述普通的数据属性,并包含以下相关属性:

    • configurable:是否可配置。如果定义为true,则该属性可以被删除,且特性如下面的enumerable、writable和value能够被修改。如果设置为false,则该属性不可被删除,也不能被修改为访问器属性。(若为false则,不可通过Object.defineProperty()二次配置属性描述符)
    • enumerable:是否可枚举遍历。如果设置为true,则该属性会出现在对象的for…in循环和Object.keys()函数的结果中。如果设置为false,则不会出现在这些结果中。
    • writable:是否可写。如果设置为true,则该属性的值可以被修改;如果设置为false,则该属性的值不能被修改,就是只读属性。
    • value:该属性的值。
  2. 存取描述符: 存取描述符用于描述getter/setter属性,并包含以下相关属性:

    • configurable:是否可配置(同上)。
    • enumerable:是否可枚举(同上)。
    • get:获取函数或方法。
    • set:设置函数或方法。

如何获取属性的属性描述符呢?

可通过 Object.getOwnPropertyDescriptor()

let obj = {
  a: 10,
  c: 20,
}
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
// configurable: true
// enumerable: true
// value: 10
// writable: true

想了解更详细的信息,详见MDN

如何配置属性描述符呢?

可通过 Object.defineProperty()

相信大多数小伙伴和我一样,看到这个方法,顿时虎躯一震。话不多说直接上代码先感受一下,它的魅力。

var obj = {};

Object.defineProperty(obj, 'myProperty', {
  configurable: true,
  enumerable: true,
  writable: false,
  value: 'example'
});


console.log(obj.myProperty); // 输出 "example"

obj.myProperty = 'new value'; // 尝试修改值

console.log(obj.myProperty); // 仍然输出 "example"

通过Object.defineProperty配置完毕,当我想重写这个属性的值时,它已经变成一个只读属性了。 当然这只是我们主角能力的冰山一角,还有我们较为熟悉的set() 和 get() 方法还没出场呢,这两大哥就不多介绍了大家都是老熟人了。

-   get() : 读取器
-   set() : 设置器 (合称:访问器)

直接上代码

    class myProperty {
      constructor(g) {

        // 定义属性
        Object.defineProperty(this, 'data', {
          get: function () {
            console.log('data被读取了');
            return g
          },
          set: function () {
            throw new Error('data属性不可重新赋值')
          },
          configurable: false, // 是否可再次配置属性描述符
        })
      }
    }
    let my = new myProperty(obj)
    let you = my.data // data被读取了 
    my.data = 'abc' // Uncaught Error: data属性不可重新赋值 at myProperty.set

在vue中get函数那步就是我们熟悉的,依赖收集,set函数就是 派发更新。

上述代码看起来非常简单是吧,但其实有两个细节是需要注意的:

  1. 在第18行中,左边的 my.data 也会触发get方法,data被读取了其实会打印两个,然后再去执行set方法,直接抛出错误。
  2. 不可以同时指定getter、setter访问器和值(value)或可写属性(writable)。否则会报错:Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object> at Function.defineProperty (<anonymous>) at new myProperty

上述代码就简单实现了,一个属性的只读功能,并会提示报错,避免写到后面了发现值没有赋上,不知道问题在哪里。当然也还是有缺陷的,只能是最表面的一层,再下一层就控制不了,那就又要用到一个新方法了。见如下扩展…

扩展1:

Object.freeze()

作用: 可以使一个对象被冻结。冻结对象可以防止扩展,并使现有的属性不可写入和不可配置。被冻结的对象不能再被更改:不能添加新的属性,不能移除现有的属性,不能更改它们的可枚举性、可配置性、可写性或值,对象的原型也不能被重新指定。freeze() 返回与传入的对象相同的对象。详见MDN。 有了这个方法我们就可以这样了。

 class myProperty {
      constructor(g) {

        let myG = {...g} // 克隆一份源数据,保证不影响数据源
        Object.freeze(myG) // 将数据冻结
        // 定义属性
        Object.defineProperty(this, 'data', {
          get: function () {
            console.log('data被读取了');
            return myG
          },
          set: function () {
            throw new Error('data属性不可重新赋值')
          },
          configurable: false, // 是否可再次配置属性描述符
        })
      }
    }
    let my = new myProperty(obj)
    let you = my.data // data被读取了 

扩展2

除了Object.freeze()方法,JavaScript还提供了一些与其类似的方法,可以用于改变对象的可变性和保护对象的数据完整性:

  1. Object.seal(obj):在原有对象上封闭(seal)该对象,并返回该对象。和freeze相比,封闭后的对象仍然可以修改属性值,但不能添加新属性,也不能删除已有属性(但是可以更改属性值)。使用这个方法可以保护对象的属性不被意外地删除。详见MDN
  2. Object.preventExtensions(obj):阻止向对象添加新属性。可以理解为冻结对象的“添加属性”功能,而不像freeze防止该对象属性值发生变化或被删除。详见MDN

需要注意的是,这些方法都只能保护对象属性的基本数据类型,对于引用类型的属性并不能完全保护。

const obj = {
  a: [1, 2, 3]
};

Object.seal(obj);
obj.a.push(4); // 可以修改数组引用类型属性

console.log(obj.a);  // 输出 [1, 2, 3, 4]


Object.preventExtensions(obj);
obj.b = 'new property'; // 不可额外添加属性

console.log(obj.b); // 输出 undefined

在上述示例中,通过Object.seal方法可以封闭obj对象并禁止额外添加新属性,但是因为a属性是一个数组,其中包含的元素并没有被冻结,所以push()方法可以用于修改数组值并且不会报错。如果要保护a属性中的元素,则需要使用递归方法冻结每个对象。

总之,这些方法都是用于JavaScript对象保护和数据完整性的工具,具有不同程度的限制和保护能力,开发者可以根据实际需求选择适合的保护方式。

当我学习这里的时候,突然感觉脑子callback了一下,理解面试官建议我去学习一下,框架或者第三方库的源码,想必大家也明白了吧,哈哈哈…

面试真是的一个查漏补缺的好方法,从面试官的一个问题,结束后的深入学习,收获知识与分享知识,真的是一个非常美妙的过程,丝毫没有觉得累和心里的不舒服。遇到一个优秀的面试官,可能真的会对自己帮助挺多的,加油,努力向一名优秀的面试官前进!

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYZ3fzjA' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片