彻底弄懂defineProperty

发布时间:2019-02-22 作者:一点通


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节点的绑定来绑定数据,在此之间还有WatcherDirective,这里不细说,接下来主要看看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
})