Object.defineProperty和proxy

283 阅读6分钟

js对象的属性

数据属性

可以直接obj.key或者obj[key]方式访问,我们平时常用的就是这种

访问器属性

包含一对儿getter和setter函数。读取访问器属性时,会调用getter函数,返回有效的值;在写入访问器属性时,会调用setter函数并传入新值。

Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

备注:应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。

/**
     * Object.defineProperty() 方法会直接在一个对象上定义一个新属性,
     * 或者修改一个对象的现有属性,并返回此对象。
     * data 要定义属性的对象。
     * key 要定义或修改的属性的名称或 Symbol 。
     * obj 要定义或修改的属性描述符。
     */
Object.defineProperty(obj, 'a', { 
    // 这里面的参数是固定的
    // configurable: false,//值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除
    // enumerable:false,//值为 true 时,该属性才会出现在对象的枚举属性中
    value:666,// 该属性对应的值
    // writable:false,//值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变
    // get:()=>{},//当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值
    // set:()=>{},//当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
 })

三个参数

data

要定义的属性对象,这个很好理解

key

要定义或修改的属性的名称或 Symbol

1. key如果是原来就有的,修改后,还是数据属性,直接打印原对象里面可以看到这个属性
2. key如果是新增的,它就是访问器属性,直接打印原对象里面不可以看到这个属性
let obj = {
    a: 1
}
Object.defineProperty(obj, 'a', { value: 2 })
Object.defineProperty(obj, 'b', { value: 3 })
console.log(obj)
console.log(obj.b)

obj 要定义或修改的属性描述符

里面的参数是固定的
Object.defineProperty(obj, 'a', { 
    // 这里面的参数是固定的
    // configurable: false,//值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除
    // enumerable:false,//值为 true 时,该属性才会出现在对象的枚举属性中
    value:666,// 该属性对应的值
    // writable:false,//值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变
    // get:()=>{},//当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值
    // set:()=>{},//当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
 })
 
set和get方法使用
let obj = {
    a: 1,
    b: 1
}
// 尝试下get
Object.defineProperty(obj, 'a', {
    get () {
        console.log('---', obj)
        return '666'
    }
})
// 尝试下set
Object.defineProperty(obj, 'b', {
    set (newValue) {
        console.log('***', newValue)
        return newValue + 1
    }
})
console.log(obj.a)
obj.b = 90
obj.b = 100
console.log(obj);

value的使用
let obj = {
    a: 1,
    b: 1
}
Object.defineProperty(obj, 'a', {
    value: 1000,
    // get () { 
    //     console.log(obj);
    // },
    // set (newVal) { 
    //     console.log(newVal)
    // }
})
console.log(obj.a)
console.log(obj)

注意value和set/get不能同时存在,会报错

let obj = {
    a: 1,
    b: 1
}
// 尝试下get
Object.defineProperty(obj, 'a', {
    value: 1000,
    get () { 
        console.log(obj);
    },
    set (newVal) { 
        console.log(newVal)
    }
})
console.log(obj.a)
console.log(obj)

proxy

  • 直译为代理,在我们访问对象前添加了一层拦截,可以过滤很多操作,例如对数据的处理,对构造函数的处理,对数据的验证等

语法

const p = new Proxy(target, handler)

参数

  • target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  • handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器)

handler属性

  1. handler.get() 方法用于拦截对象的读取属性操作。
let obj = {
    a: 1
}
// 参数一是需要处理的对象
// 参数二是对处理的对象的配置,参数是固定的
let test = new Proxy(obj, {
    /**
     * target目标对象
     * property将被设置的属性名或 Symbol
     * receiver最初被调用的对象
     * target, property,  receiver
     * 目标,属性,接收者
     */
    get (target, property, receiver) {
        console.log('对obj做查询操作都会进来,这里做逻辑操作', target, property, receiver)
        return 666 // 返回的值自由控制
    }
})
console.log(test.a)

  1. handler.set() 方法用于拦截设置属性值的操作

注意,set方法需要返回一个布尔值

let obj = {
    a: 1
}
// 参数一是需要处理的对象
// 参数二是对处理的对象的配置,参数是固定的
let test = new Proxy(obj, {
    /**
     * target目标对象
     * property将被设置的属性名或 Symbol
     * value新属性值
     * receiver最初被调用的对象
     * target, property, value, receiver
     * 目标,属性,值,接收者
     */
    set (target, property, value, receiver) {
        console.log('对obj做修改操作都会进来,这里做逻辑操作', target, property, value, receiver)
        target[property] = value // 将新属性值赋给目标对象
        return true // set() 方法应当返回一个布尔值。
    }
})
test.b = 666
console.log(test)

call方法

官网解释

调用一个对象的一个方法,以另一个对象替换当前对象

作用

  1. 继承

  2. 修改函数运行时this的指向

Reflect

官网解释

eflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect不是一个函数对象,因此它是不可构造的。Reflect的所有属性和方法都是静态的(就像Math对象)。

静态方法

  1. get(),Reflect.get方法允许你从一个对象中取属性值。就如同属性访问器 语法,但却是通过函数调用来实现。
// 获取对象身上某个属性的值,类似于 target[name]。
// 返回值 属性的值
// 参数     目标对象 需要获取的值的键值  如果target对象中指定了getter,receiver则为getter调用时的this值。
Reflect.get(target, propertyKey[, receiver])
  1. set(),Reflect.set 方法允许你在对象上设置属性。它的作用是给属性赋值并且就像 property accessor 语法一样,但是它是以函数的方式。
// 在一个对象上设置一个属性。
// 返回值 返回一个 Boolean 值表明是否成功设置属性。
// 参数     目标对象 属性的名称    设置的值  如果遇到 setter,receiver则为setter调用时的this值。
Reflect.set(target, propertyKey, value[, receiver])

contain方法

  • 判断当前dom是否包含另一个dom
A.contains(B) // 包含为true,不包含为false