Skip to content

描述符和代理类

描述符

Object.defineProperty()

与 getter 和 setter 功能更高级。对对象的属性添加和修改(劫持)

需要传入 3 个参数:

  1. obj:要在其上定义属性的对象。
  2. prop:要定义或修改的属性的名称。
  3. descriptor:将被定义或修改的属性描述符。

descriptor 对象的可定义的属性:

  • 值(value)

  • 值可修改性(writable)

    属性值是否可以修改,改成 false 就是不可以修改

  • 可枚举性(enumerable)

    属性值是否可以枚举,当我们挨个访问属性值的时候可以访问得到与否

  • 属性特点可修改性(configurable)

    上述属性的特点是可以修改改的,如果设置 false,则上述可修改可枚举以及本身都不可以修改了,相当于被直接冻结

value 和 writable

javascript
let a = {
    like: "food",
    name: "Gin",
}
Object.defineProperty(a, "like", {
    value: "goods",
    writable: false,
})
console.log(a.like) //goods
a.like = "123"
console.log(a.like) //goods

enumerable

可枚举指的是是否能够被for……in或者Object.keys遍历到,默认为true

javascript
let a = {
    like: "food",
    name: "Gin",
}
Object.defineProperty(a, "like", {
    value: "goods",
    writable: false,
    enumerable: false,
})
for (i in a) {
    console.log(i)
} //"name"
Object.keys(a) //["name"]

注意:

给数组元素设置enumerable: false可以不被上文的两种方法遍历到,
但是能否被for...of遍历到,取决于它是否是生成器中一个 yield 的值。

get 和 set

在对属性操作(获取,修改)的时候会触发的操作,
不能 同时设置访问器 (getset) 和wriablevalue

javascript
let a = {}
Object.defineProperty(a, "like", {
    get: (function() {
        var t = 0
        return function() {
            return t++
        }
    })(),
})
//每次访问就会增加1,从0开始,并且不可修改
javascript
let a = {}
Object.defineProperty(a, "like", {
    get: function() {
        return this._t
    },
    set: function(newValue) {
        console.log("你正在设置like的值")
        return (this._t = newValue)
    },
})
a.like = 2 //"你正在设置like的值"
console.log(a.like)

它可以帮助我们实现数据变动的监听。但是存在一个可以访问的私有变量。
Vue2 的双向数据绑定的原理就是借助这个方法。

除此之外还有 Object.defineProperties

javascript
var test = {}

Object.defineProperties(test, {
    o: {
        get() {},
        set(v) {},
    },
    p: {
        get() {},
        set(v) {},
    },
})

Object.create

创建新对象,第二个参数与描述符相仿

在传入一个参数的情况下,与 Object()方法的行为相同。

javascript
//创建一个原型为{}的对象
//有一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
    p: {
        value: 42,
        writable: true,
        enumerable: true,
        configurable: true,
    },
})

它还可以作为类式继承(原型式继承)的规范写法

浅拷贝

拷贝对象使用Object.assign()只能拷贝到可枚举属性,要完美的浅拷贝可以使用:

javascript
Object.create(
    Object.getPrototypeOf(obj), //constructor
    Object.getOwnPropertyDescriptors(obj) //包括描述符的所有属性
)

Proxy 和 Reflect

Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。

代理类 Proxy

const p = new Proxy(target, handler)

Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。
Proxy 不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

提示

这里只示例最简单的组合用法,更多用法可以在MDN中查看有关文档。

javascript
let n = {
    a: 0,
    b: 1,
}

let m = new Proxy(n, {
    get(target, key, value, proxy) {
        return Reflect.get(target, key, value, proxy)
    },
    set(target, key, value, proxy) {
        console.log("代理的对象是", target)
        console.log("被修改的键是", key)
        console.log("其值将会被修改为", value)
        return Reflect.set(target, key, value, proxy)
    },
})

m.a = 8
console.log(n.a)

其中 m 是 n 的代理对象 m 和 n 中间构成了所谓的单向数据绑定

如果代理的对象是数组,则在调用一次数组方法时可能会执行多次 set 方法,因为 length 也会单独触发 set。

另外,直接n = new Proxy(n, {...})可以覆盖自身

反映类 Reflect

Reflect 为操作对象提供了一种更优雅的方式,它使用函数的方式实现了 Object 的命令式操作。其方法与 Proxy 的 handler 参数是对应的。

检测一个对象是否存在特定属性

javascript
Reflect.has(duck, "color")

为对象添加一个新的属性

javascript
Reflect.set(duck, "eyes", "black")

更多见MDN