美文网首页
proxy&reflect

proxy&reflect

作者: Super曲江龙Kimi | 来源:发表于2020-02-04 10:49 被阅读0次

Proxy

proxy能够代理整个对象, 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

和defineProperties的对比

1 :defineProperties是针对属性的劫持。而proxy是针对于对象的,不需要递归进行劫持。性能更好。
2: proxy可以代理数组,而不需要重写数组的方法(hack)。length变化也可以监控到。而defineProperties不行
3: proxy代理支持13种代理拦截方法,而defineProperties没有那么多。
4: proxy返回的是一个新对象,而defineProperties只能遍历操作源对象
5: proxy兼容性不好,需要polyfill。

基本操作

get

const arr = [111,222,44,33];
const proxyArr = new Proxy(arr, {
    get(target, key, _proxy) {
        console.log(target === arr);   // true
        console.log(target === proxyArr); // false  返回新对象
        console.log(_proxy === proxyArr); // true
        console.log(_proxy === arr); // false 
        let index = key;
        if (key < 0) {
            index = target.length + Number(key);
        }
        return target[index];
    }
})
console.log(proxyArr[-4])  //  111

如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错

set

const good = {
    name : 'car',
    price: 100
}
const proxyGood = new Proxy(good, {
    set(target, key, value, _proxy) {
        if (Number(value) > target.price) {
            Reflect.set(target, key, value, _proxy);
        } else {
            throw new RangeError('price is invalid');
        }
    }
})
proxyGood.price = 500; // { name:'car', price:500 }
proxyGood.price = 300; // RangeError('price is invalid')

注意,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。

apply

var twice = {
    apply (target, ctx, args) {
        console.log(arguments) // [target, ctx, args]
        console.log(args) // target函数执行时参数列表
        console.log(ctx) // 上下文this
        console.log(target === sum) // true
        return Reflect.apply(...arguments) * 2;
    }
};
function sum (left, right) {
    return left + right;
};
var proxy = new Proxy(sum, twice);
console.log(proxy(1, 2)); // 6
console.log(proxy.call(null, 5, 6)) // 22
console.log(proxy.apply(null, [7, 8])); // 30
console.log(proxy.bind({}, 5, 5)()); // 20
console.log(Reflect.apply(proxy, null, [6, 6])); // 24

has

has 只对 in操作符生效, 但不隐藏属性,所以for in 、keys等操作仍然可以遍历到属性

const stu1 = {name: 'kimi', score: 80};
const stu2 = {name: 'boom', score: 59};

const handle = {
    has(target, key) {
        if (key === 'score' && target[key] < 60) return false;
        return true;
    }
}

const proxy1 = new Proxy(stu1, handle);
const proxy2 = new Proxy(stu2, handle);

console.log('score' in proxy1) // true
console.log('score' in proxy2) // false
for (let k in proxy2) {
    console.log(k) // name score 
    console.log(proxy2[k]) // boom 59
}
console.log(Object.keys(proxy2)); // [ 'name', 'score' ]

如果原对象不可配置或者禁止扩展,这时has拦截会报错。如果某个属性不可配置(或者目标对象不可扩展),则has方法就不得“隐藏”目标对象的该属性。

construct

const p = new Proxy(Array, {
    construct(target, args, _proxy) {
        console.log(_proxy === p); // true
        return { val : new target(...args) } // 必须返回一个对象,如果不是会报错
    }
})

const a = new p(222,33);
console.log(Object.getPrototypeOf(a));

deleteProperty

const a = {age: 27}
const b = {age: 35}

function checkAge(age) {
    if (Number(age) > 30) throw new RangeError('age is too old!')
}

const handle = {
    deleteProperty(target, key) {
        checkAge(target[key]);
        Reflect.deleteProperty(target, key);
        return true
    }
}

const p1 = new Proxy(a, handle)
const p2 = new Proxy(b, handle)

console.log(delete p1.age); // true
console.log(delete p2.age); // new RangeError('age is too old!')

注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。

ownKeys

const p = new Proxy({'name':'kimi'}, {
    ownKeys(target) {
        // ownKeys方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。
        return ['a', 'name']
    }
})

for(let k in p) {
    // 拦截后只返回['a', 'name'],但是原来的对象中只有name属性所以只返回name没有a
    console.log(k);  // name  
}
console.log(Object.keys(p)); // ['name']  与for in 同理
console.log(Reflect.ownKeys(p)); // ['a', 'name']
console.log(Object.getOwnPropertyNames(p)); // ['a', 'name']

注意,使用Object.keys方法和for in时,有三类属性会被ownKeys方法自动过滤,不会返回。

  1. 目标对象上不存在的属性
  2. 属性名为 Symbol 值
  3. 不可遍历(enumerable)的属性
let target = {
  a: 1,
  b: 2,
  c: 3,
  [Symbol.for('secret')]: '4',
};

Object.defineProperty(target, 'key', {
  enumerable: false,
  configurable: true,
  writable: true,
  value: 'static'
});

let handler = {
  ownKeys(target) {
    return ['a', 'd', Symbol.for('secret'), 'key'];
  }
};

let proxy = new Proxy(target, handler);

Object.keys(proxy) // ['a']
for(let k in proxy) {
      console.log(k); // a
 }

proxy中还可以拦截 defineProperty、getOwnPropertyDescriptor、getPrototypeOf
、isExtensible、preventExtensions、setPrototypeOf等方法。

Reflect

Reflect的作用是将Object对象的一些明显属于语言内部的方法,放到Reflect对象上。比如Object.defineProperty。这种属于整个语言的方法,并且修改某些Object方法的返回结果,让其变得更合理。方法和proxy一一对应。

Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和

相关文章

  • proxy&reflect

    Proxy proxy能够代理整个对象, 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截...

网友评论

      本文标题:proxy&reflect

      本文链接:https://www.haomeiwen.com/subject/ucskxhtx.html