每周分享:彻底弄懂 defineProperty

Vue.js 的流行将其数据双向绑定的核心带到了大家面前,那就是 Object.defineProperty(),在详细讲解这个方法之前,我们先用一段最简单的代码实现一个最简单的数据响应。

<div id="main"></div>
let info = {}
Object.defineProperty(info, 'name', {
  get: function () {
    return document.getElementById('main').innerHTML
  },
  set: function (value) {
    document.getElementById('main').innerHTML = value
  }
})
info.name = "hello" 

这里当我们修改 info.name 的值的时候就会动态反馈到 HTML 元素上。这里的核心就是 set 定义了在给 info 对象设置值的时候会自动去执行 set 中的代码,从而达到了数据绑定的效果。起到了一个触发器的效果。

当然这仅仅是一个简单的示例,实际上 Vue.js 并不是单纯地通过数据和 DOM 节点的绑定来绑定数据,在此之间还有 WacherDirective,这里不细说,接下来主要看看 Object.defineProperty() 到底是什么。

作用

给指定对象加上新的属性或者修改原有属性的值

返回值

被操作的对象

语法

Object.defineProperty(obj, prop, descriptor)

参数

  • obj:要操作的对象
  • prop:要修改或新增的属性名称
  • descriptor:属性描述符

接下来着重讲讲属性描述符

属性描述符

属性描述符(descriptor) 顾名思义就是如何描述(设置)指定属性,分为两种:数据描述符存取描述符

数据描述符

可选键值有

value

要设置的具体值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined

writable

规定该属性是否可被赋值运算符(=)改变。默认为 false

var obj = {}
Object.defineProperty(obj, 'name', {
  value: 'lucy',
  writable: false
})

那么 obj{name: "lucy"},然后我们再修改

obj.name = 'tom'

此时 obj 仍然是 {name: "lucy"} ,因为我们定义了 name 属性的 writablefalse 是不能被赋值运算符修改的(注意这里并不会报错)。当然不设置 writable 的值也是可以的,因为其默认值就是 false

存取描述符

可选键值有

get

一个给属性提供 getter 的方法。当访问该属性时,该方法会被执行,默认为 undefined

set

一个给属性提供 setter 的方法,当属性值修改时,触发执行该方法,默认为 undefined

这俩其实就是在给属性赋值或者取值的时候的一个 触发器

var obj = {}
Object.defineProperty(obj, 'name', {
  set: function (value) {
  },
  get: function () {
  }
})

公共键值

除了上面两种描述符各自独有的键值,他们还有公共的键值可用

configurable

表示该属性是否可以被删除,以及除 writable 特性外的其他特性是否可以被修改。默认为 false

enumerable

定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。默认为 false

var obj = {
  age: 12
}
Object.defineProperty(obj, 'name', {
  value: 'tom',
  enumerable: false
})

这里 obj 变成了 {age: 12, name: "tom"} 但是我们执行

Object.keys(obj)

得到的却是 ["age"] ,可见 enumerable 确实影响到了 Object.keys() 的取值。当然直接写成下面这样也是可以的

Object.defineProperty(obj, 'name', {
  value: 'tom'
})

因为 enumerable 的默认值就是 false

注意,如果同时设置了数据描述符和存取描述符,是会出现异常的,比如我这样写

var obj = {}
Object.defineProperty(obj, 'name', {
  set: function (value) {
  },
  value: 'tom'
})

则会报错

 Invalid property descriptor. Cannot both specify accessors and a value or writable attribute

数据描述符默认值

需要注意的是,以两种不同的方式给属性赋值时,数据描述符中的属性默认值是不同的。

1、点运算

var obj = {}
obj.name = 'tom'

等价于

Object.defineProperty(obj, 'name', {
  value : 'tom',
  writable : true,
  configurable : true,
  enumerable : true
})

2、Object.defineProperty()

Object.defineProperty(obj, 'name', {
  value : 'tom',
})

等价于

Object.defineProperty(obj, 'name', {
  value : 'tom',
  writable : false,
  configurable : false,
  enumerable : false
})

最新评论

  1. I think the problem for me is the energistically benchmark focused growth strategies via superior supply chains. Compellingly reintermediate mission-critical potentialities whereas cross functional scenarios. Phosfluorescently re-engineer distributed processes without standardized supply chains. Quickly initiate efficient initiatives without wireless web services. Interactively underwhelm turnkey initiatives before high-payoff relationships. Felix Youngkin

  2. I was wondering if you ever thought of changing the structure of your blog? Its very well written; I love what youve got to say. But maybe you could a little more in the way of content so people could connect with it better. Youve got an awful lot of text for only having one or 2 images. Maybe you could space it out better? Alphonse Kivisto

  3. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Roman Toda

  4. Everything is very open with a very clear explanation of the challenges. It was truly informative. Your site is very useful. Thank you for sharing. Francisco Swonger

发表评论

邮箱地址不会被公开。 必填项已用*标注