初识Proxy、Reflect

前言

https://segmentfault.com/a/1190000009789481#articleHeader2

Vue3.0应该马上就要发布正式版了。听说在新版本中,Proxy取代了Object.defineProperty进行双向绑定。主要原因应该是Object.defineProperty在处理数组响应是会存在缺陷。

let demo = {};
let arr = [];

Object.defineProperty(demo, "arr", {
  get: function() {
    return arr;
  },
  set: a => {
    console.log("hear set");
    arr = a;
  },
  configurable: true,
  enumerable: true
});

demo.arr = [1, 2, 3, 4]; // => 'hear set';
console.log(arr === demo.arr); // => true
/* 通过索引修改数组,无法触发set */
demo.arr[2] = null; // => 没有输出hear set。
console.log(arr === demo.arr); // => true
/* 通过push,pop,sort等方法同样无法触发set */
demo.arr.push("13"); // => 没有输出hear set。

Edit static

Proxy

http://es6.ruanyifeng.com/#docs/proxy

示例

Proxy原意为代理,在实际操作中,可以理解为,在目标数据前增加一个拦截器。通过拦截器,可以实现监听、修改目标数据。

let obj = new Proxy(
  {},
  {
    set: function(target, key, receiver) {
      console.log(`set key ${key}`);
      return Reflect.set(target, key, receiver);
    },
    get: function(target, key, receiver) {
      console.log(`get key ${key}`);
      return Reflect.get(target, key, receiver);
    }
  }
);

obj.count = 1;
// => set key count;
obj.count++;
// => get key count;
// => set key count;

Edit static

构造函数

let obj = new Proxy(target, handler)

其中new Proxy表示生成一个Proxy实例,target为需要代理的对象,handler则是一个对象,定义了各种代理行为。
如果handler为空对象,访问proxy与访问target效果相同。

Proxy的实例方法

get()

get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
  get: function(target, property) {
    if (property in target) {
      return target[property];
    } else {
      throw new ReferenceError("Property \"" + property + "\" does not exist.");
    }
  }
});

proxy.name // "张三"
proxy.age // 抛出一个错误

set()

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

has()

has(target, propKey)拦截propKey in proxy的操作,返回一个布尔值。

deleteProperty()

deleteProperty(target, propKey)拦截delete proxy[propKey]的操作,返回一个布尔值。

ownKeys()

ownKeys(target)拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

getOwnPropertyDescriptor()

getOwnPropertyDescriptor(target, propKey)拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

###defineProperty()
defineProperty(target, propKey, propDesc)拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

preventExtensions()

preventExtensions(target)拦截Object.preventExtensions(proxy),返回一个布尔值。

getPrototypeOf()

getPrototypeOf(target)拦截Object.getPrototypeOf(proxy),返回一个对象。

isExtensible()

isExtensible(target)拦截Object.isExtensible(proxy),返回一个布尔值。

setPrototypeOf()

setPrototypeOf(target, proto)拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

apply()

apply(target, object, args)拦截Proxy实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)、proxy.apply(...)

construct()

construct(target, args)拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)

Reflect

http://es6.ruanyifeng.com/#docs/reflect#实例:使用-Proxy-实现观察者模式

简介

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。其目的是:

  1. Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
  2. 修改某些Object方法的返回结果,让其变得更合理。
  3. Object操作都变成函数行为。
  4. Reflect对象的方法与Proxy对象的方法一一对应

使用Proxy监听数组

    let arr = new Proxy([], {
      set: function (target, key, value) {
        debugger
        console.log(`hear ${key}`)
        if (parseFloat(key).toString() !== 'NaN' && key >= 0 && key % 1 === 0) {
          // 合法的索引:整数,大于等于零
          Reflect.set(target, key, value);
          return true
        }
        if (key === 'length') {
          Reflect.set(target, key, value);
          return true
        }
        throw new ReferenceError("非法插入")
      }
    })

Edit static

使用Proxy、Reflect实现观察者模式

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, { set });
function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach(observer => observer());
    return result;
}
const person = observable({
    name: "Amber",
    age: 18
});
function print() {
    console.log(`${person.name}, ${person.age}`);
}
observe(print);

Edit static

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值