Object.defineProperty()的使用

栏目: 编程语言 · 发布时间: 5年前

内容简介:Object.defineProperty(),它的作用是可以通过该API直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。在平时的业务开发中,前俩个参数很简单明了,就是指出要在哪个对象上定义或修改哪个属性。重要的是属性的描述符,当我们通过

Object.defineProperty(),它的作用是可以通过该API直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。 Vue 框架内部大量使用了此API为对象定义属性,其响应式原理也是通过此API自定义setter与getter而完成的。

在平时的业务开发中, Object.defineProperty() 基本上使用不到,因为在对象上定义一个新属性直接通过 . 运算符就可以,例: obj.a = 123 。但通过该API可以对一个对象属性做更多的事情,比如数据的拦截、自定义setter与getter等。

概念

Object.defineProperty(obj, prop, descriptor)
  • obj :要在其上定义属性的对象
  • prop :要定义或修改的属性的名称
  • descriptor :将被定义或修改的属性描述符

前俩个参数很简单明了,就是指出要在哪个对象上定义或修改哪个属性。重要的是属性的描述符,当我们通过 . 运算符定义或修改属性的时候,其实等同于调用了 Object.defineProperty() 。如下:

const man = {}
man.name = 'lihaoze'
Object.getOwnPropertyDescriptor(man, 'name')
// {value: "lihaoze", writable: true, enumerable: true, configurable: true}
复制代码
const man = {}
Object.defineProperty(man, 'name', {
  value: 'lihaoze',
  writable: true,
  configurable: true,
  enumerable: true
})
Object.getOwnPropertyDescriptor(man, 'name')
// {value: "lihaoze", writable: true, enumerable: true, configurable: true}
复制代码

value 顾名思义就是属性对应的值,但其不一定是必须存在的。它与setter、getter互斥。后面我们会讲到。那 writable , enumerable , configurable 具体是什么用处呢,我们接下来分别介绍。

writable

当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。

当我们通过 Object.defineProperty() 定义一个属性不设置 writable 属性,或者设置为false。那么我们将不能通过 . 的方式来修改属性值。

const man = { name: 'lihaoze' }
Object.defineProperty(man, 'age', {
  value: 18,
  writable: false,
  configurable: true,
  enumerable: true
})
man.age = 22
alert(man.age) // 18
复制代码

可以看到我永远都是18岁了:stuck_out_tongue_winking_eye:,但总有人想试图揭穿我,所以Ta只要再使用该API重新定义 value ,就可以修改它。如下

Object.defineProperty(man, 'age', {
  value: 22,
  writable: false,
  configurable: true,
  enumerable: true
})
alert(man.age) // 22
复制代码

configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。所以,当configurable 为 false的时候,该属性的描述符就不能被修改了,也不能被删除。这是个不可逆的操作。

const man = { name: 'lihaoze' }
Object.defineProperty(man, 'age', {
  value: 18,
  writable: true,
  configurable: false,
  enumerable: true
})
man.age = 22
console.log(man.age) // 22
delete man.age
// 无法删除该属性 
console.log(man.age) // 22 
// 尝试修改enumerable,无法再次修改描述符 Cannot redefine property: age
Object.defineProperty(man, 'age', {
  value: 18,
  writable: true,
  configurable: false,
  enumerable: false 
})
复制代码

但这里有个例外:当writable属性为true的时候,是可以修改成false的。看下面代码:

// 这时,只有writable可以被修改成false,但false之后,就无法再修改成true
Object.defineProperty(man, 'age', {
  value: 18,
  writable: false,
  configurable: false,
  enumerable: true
})
man.age = 22
console.log(man.age) // 18
复制代码

enumerable

当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。 可以直到,这个描述符控制的是属性是否会出现在对象的属性枚举中,比如 for..in 循环,如果把 enumerable 设置成false,这个属性不会出现在枚举中,虽然仍然可以访问它。

const man = { name: 'lihaoze', age: 18 }
Object.defineProperty(man, 'job', {
  value: 'Web Engineer',
  writable: false,
  configurable: true,
  enumerable: false 
})
console.log(man) // {name: "lihaoze", age: 18, job: "Web Engineer"}
console.log(Object.keys(man)) // ["name", "age"]
for (let key in man) {
  console.log(key) // name age
}
复制代码

set、get

set: 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。

get: 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象

注意:当定义了一个属性的set、get描述符,则JavaScript会忽略该属性的value、writable属性。也就是说这俩对儿属于互斥的关系

const man = { name: 'lihaoze', birthYear: 1996 }
// 定义setter、getter,
// get、set方法不能使用本身属性,会造成堆栈溢出 Maximum call stack size exceeded
Object.defineProperty(man, 'age', {
  configurable: true,
  enumerable: true,
  get() {
    const date = new Date()
    return date.getFullYear() - this.birthYear
  },
  set(val) {
    // Uncaught Error: 无法修改真实年龄
    throw new Error('无法修改真实年龄')
  }
})
复制代码

上面的例子通过出生年份来推算我的年龄,当设置年龄的时候,我们抛出错误,防止被修改。

// 尝试定义value、writable描述符,会抛出错误
// Uncaught TypeError: Invalid property descriptor. 
// Cannot both specify accessors and a value or writable attribute
Object.defineProperty(man, 'age', {
  value: 15,
  configurable: true,
  enumerable: true,
  get() {
    const date = new Date()
    return date.getFullYear() - this.birthYear
  },
  set(val) {
    // Uncaught Error: 无法修改真实年龄
    throw new Error('无法修改真实年龄')
  }
})
复制代码

应用

  • 通过设置set描述符,来终止其他人修改值,并给出于友好的提示,如上面的代码⇧。其中Vue内部也是使用这个方法,来给出我们开发者友好的提示。举几个列子:

    // 我们不用去弄懂defineReactive,只要知道该函数的第四个参数是定义的get函数,
    // 可以看到Vue在非生产环境,会为$attrs、props定义get,防止用户修改该属性,给出提示。
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
    }, true)
    defineReactive(props, key, value, () => {
      if (!isRoot && !isUpdatingChildComponent) {
        warn(
          `Avoid mutating a prop directly since the value will be ` +
          `overwritten whenever the parent component re-renders. ` +
          `Instead, use a data or computed property based on the prop's ` +
          `value. Prop being mutated: "${key}"`,
          vm
        )
      }
    })
    复制代码
  • 通过设置get描述符,来代理对象上面的值,在Vue中我们之所以可以使用 this.xxx 访问各种数据、方法、props、是因为Vue将这些都设置了set,从而代理到其他私有对象上。看下面代码:

    const vm = new Vue({
      el: '#app'
      data: {
        msg: 'hello world!'
      }
    })
    vm.msg = vm._data.msg // true
    复制代码

    其中,Vues是通过 proxy 函数实现数据代理,Vue部分源码如下:

    // 定义通用描述符
    const sharedPropertyDefinition = {
      enumerable: true,
      configurable: true,
      get: noop,
      set: noop
    }
    // 设置set、get、实现将this[sourceKey]上的值代理到this[key]
    export function proxy (target: Object, sourceKey: string, key: string) {
      sharedPropertyDefinition.get = function proxyGetter () {
        return this[sourceKey][key]
      }
      sharedPropertyDefinition.set = function proxySetter (val) {
        this[sourceKey][key] = val
      }
      Object.defineProperty(target, key, sharedPropertyDefinition)
    }
    // ... 忽略无关代码
    proxy(vm, `_data`, key)
    复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具