construct()

construct方法用于拦截new命令。

var handler = {
construct (target, args) {
return new target(...args);
}
}

下面是一个例子。

var p = new Proxy(function() {}, {
construct: function(target, args) {
console.log('called: ' + args.join(', '));
return { value: args[0] * 10 };
}
}); new p(1).value
// "called: 1"
// 10

如果construct方法返回的不是对象,就会抛出错误。

var p = new Proxy(function() {}, {
construct: function(target, argumentsList) {
return 1;
}
}); new p() // 报错
deleteProperty()

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
return true;
}
}
function invariant (key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
} var target = { _prop: 'foo' }
var proxy = new Proxy(target, handler)
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property

上面代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。

defineProperty()

defineProperty方法拦截了Object.defineProperty操作。

var handler = {
defineProperty (target, key, descriptor) {
return false
}
}
var target = {}
var proxy = new Proxy(target, handler)
proxy.foo = 'bar'
// TypeError: proxy defineProperty handler returned false for property '"foo"'

上面代码中,defineProperty方法返回false,导致添加新属性会抛出错误。

enumerate()

enumerate方法用来拦截for...in循环。注意与Proxy对象的has方法区分,后者用来拦截in操作符,对for...in循环无效。

var handler = {
enumerate (target) {
return Object.keys(target).filter(key => key[0] !== '_')[Symbol.iterator]();
}
}
var target = { prop: 'foo', _bar: 'baz', _prop: 'foo' }
var proxy = new Proxy(target, handler)
for (let key in proxy) {
console.log(key);
// "prop"
}

上面代码中,enumerate方法取出原对象的所有属性名,将其中第一个字符等于下划线的都过滤掉,然后返回这些符合条件的属性名的一个遍历器对象,供for...in循环消费。

下面是另一个例子。

var p = new Proxy({}, {
enumerate(target) {
console.log("called");
return ["a", "b", "c"][Symbol.iterator]();
}
}); for (var x in p) {
console.log(x);
}
// "called"
// "a"
// "b"
// "c"

如果enumerate方法返回的不是一个对象,就会报错。

var p = new Proxy({}, {
enumerate(target) {
return 1;
}
}); for (var x in p) {} // 报错
getOwnPropertyDescriptor()

getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor,返回一个属性描述对象或者undefined

var handler = {
getOwnPropertyDescriptor (target, key) {
if (key[0] === '_') {
return
}
return Object.getOwnPropertyDescriptor(target, key)
}
}
var target = { _foo: 'bar', baz: 'tar' };
var proxy = new Proxy(target, handler);
Object.getOwnPropertyDescriptor(proxy, 'wat')
// undefined
Object.getOwnPropertyDescriptor(proxy, '_foo')
// undefined
Object.getOwnPropertyDescriptor(proxy, 'baz')
// { value: 'tar', writable: true, enumerable: true, configurable: true }

上面代码中,handler.getOwnPropertyDescriptor方法对于第一个字符为下划线的属性名会返回undefined。

getPrototypeOf()

getPrototypeOf方法主要用来拦截Object.getPrototypeOf()运算符,以及其他一些操作。

  • Object.prototype.proto
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof运算符

下面是一个例子。

var proto = {};
var p = new Proxy({}, {
getPrototypeOf(target) {
return proto;
}
});
Object.getPrototypeOf(p) === proto // true

上面代码中,getPrototypeOf方法拦截Object.getPrototypeOf(),返回proto对象。

isExtensible()

isExtensible方法拦截Object.isExtensible操作。

var p = new Proxy({}, {
isExtensible: function(target) {
console.log("called");
return true;
}
}); Object.isExtensible(p)
// "called"
// true

上面代码设置了isExtensible方法,在调用Object.isExtensible时会输出called。

这个方法有一个强限制,如果不能满足下面的条件,就会抛出错误。

Object.isExtensible(proxy) === Object.isExtensible(target)

下面是一个例子。

var p = new Proxy({}, {
isExtensible: function(target) {
return false;
}
}); Object.isExtensible(p); // 报错
ownKeys()

ownKeys方法用来拦截Object.keys()操作。

let target = {};

let handler = {
ownKeys(target) {
return ['hello', 'world'];
}
}; let proxy = new Proxy(target, handler); Object.keys(proxy)
// [ 'hello', 'world' ]

上面代码拦截了对于target对象的Object.keys()操作,返回预先设定的数组。

]

下面的例子是拦截第一个字符为下划线的属性名。

var target = {
_bar: 'foo',
_prop: 'bar',
prop: 'baz'
}; var handler = {
ownKeys (target) {
return Reflect.ownKeys(target).filter(key => key[0] !== '_');
}
}; var proxy = new Proxy(target, handler);
for (let key of Object.keys(proxy)) {
console.log(key)
}
// "baz"
preventExtensions()

preventExtensions方法拦截Object.preventExtensions()。该方法必须返回一个布尔值。

这个方法有一个限制,只有当Object.isExtensible(proxy)为false(即不可扩展)时,proxy.preventExtensions才能返回true,否则会报错。

var p = new Proxy({}, {
preventExtensions: function(target) {
return true;
}
}); Object.preventExtensions(p); // 报错

上面代码中,proxy.preventExtensions方法返回true,但这时Object.isExtensible(proxy)会返回true,因此报错。

为了防止出现这个问题,通常要在proxy.preventExtensions方法里面,调用一次Object.preventExtensions。

var p = new Proxy({}, {
preventExtensions: function(target) {
console.log("called");
Object.preventExtensions(target);
return true;
}
}); Object.preventExtensions(p)
// "called"
// true
setPrototypeOf()

setPrototypeOf方法主要用来拦截Object.setPrototypeOf方法。

下面是一个例子。

var handler = {
setPrototypeOf (target, proto) {
throw new Error('Changing the prototype is forbidden');
}
}
var proto = {};
var target = function () {};
var proxy = new Proxy(target, handler);
proxy.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden

上面代码中,只要修改target的原型对象,就会报错。

Proxy.revocable()

Proxy.revocable方法返回一个可取消的Proxy实例。

let target = {};
let handler = {}; let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123;
proxy.foo // 123 revoke();
proxy.foo // TypeError: Revoked

Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。上面代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。

Reflect概述

Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。

(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
} // 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}

(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。+

// 老写法
'assign' in Object // true // 新写法
Reflect.has(Object, 'assign') // true

(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target,name, value, receiver);
if (success) {
log('property ' + name + ' on ' + target + ' set to ' + value);
}
return success;
}
});

上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,然后再部署额外的功能。

下面是另一个例子。

var loggedObj = new Proxy(obj, {
get(target, name) {
console.log('get', target, name);
return Reflect.get(target, name);
},
deleteProperty(target, name) {
console.log('delete' + name);
return Reflect.deleteProperty(target, name);
},
has(target, name) {
console.log('has' + name);
return Reflect.has(target, name);
}
});

上面代码中,每一个Proxy对象的拦截操作(get、delete、has),内部都调用对应的Reflect方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。

有了Reflect对象以后,很多操作会更易读。

// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1 // 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1
Reflect对象的方法
  • Reflect对象的方法清单如下,共14个。
  • Reflect.apply(target,thisArg,args)
  • Reflect.construct(target,args)
  • Reflect.get(target,name,receiver)
  • Reflect.set(target,name,value,receiver)
  • Reflect.defineProperty(target,name,desc)
  • Reflect.deleteProperty(target,name)
  • Reflect.has(target,name)
  • Reflect.ownKeys(target)
  • Reflect.enumerate(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

ES6 Proxy和Reflect(下)的更多相关文章

  1. ES6(Proxy 和 Reflect)

    Proxy 和 Reflect 1.Proxy 和 Reflect 的概念 Proxy 意为 ‘代理’,连接了用户和真实对象之间的一个层 Reflect 意为‘反射’   反射的是Object 2.适 ...

  2. ES6 Proxy和Reflect (上)

    Proxy概述 Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程. Proxy可以理 ...

  3. es6——Proxy和Reflect

    Proxy代理,Reflect反射 Proxy对属性的读取 { //供应商,原始对象 let obj={ time:'2017-1-1', name:'net', _r:123 } //代理商,新生成 ...

  4. 利用ES6中的Proxy和Reflect 实现简单的双向数据绑定

    利用ES6中的Proxy (代理) 和 Reflect 实现一个简单的双向数据绑定demo. 好像vue3也把 obj.defineProperty()  换成了Proxy+Reflect. 话不多说 ...

  5. ES6入门:数据劫持、Proxy、Reflect

    什么是数据劫持 Object数据劫持实现原理 Array数据劫持的实现原理 Proxy.Reflect 一.什么是数据劫持 定义:访问或者修改对象的某个属性时,在访问和修改属性值时,除了执行基本的数据 ...

  6. es6之proxy和reflect

    一.proxy //Proxy和Reflect //供应商 let obj={ time:"2017-11-21", name:"net", _r:123 } ...

  7. [ES6] Proxy & Reflect

    Proxy and Reflect API works nicely together. About how to use Proxy, check this post. Let's see abou ...

  8. 12,13 Proxy和Reflect

    Proxy和Reflect Proxy(代理) Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程 ...

  9. babel 不能统编译Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise的问题

    Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator.Generator.Set.Maps.Proxy.Reflect.Symbol.Promis ...

随机推荐

  1. Python之文件的基本操作

    在python中,对文件的基本操作一共有如下四种: 1.打开文件 file_obj = open("文件路径","模式") 常用的打开文件模式有: r:以只读方 ...

  2. webpack构建项目

    webpack构建项目 案例代码戳这里 ps:每个案例对应相应的demo,例如"案例1"对应"demo1" 一.webpack基本功能及简单案例 安装webpa ...

  3. HDU-1242-Rescu

    Rescue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  4. jdk7jdk8新特性概述

    在oracle停止对jdk6更新,jdk8发布之后,公司终于要把生产环境更新到jdk7,下面列一下jdk7,8的可能需要关注的新特性. jdk7 G1垃圾回收 fork-join框架 二进制变量 Sw ...

  5. 》》css3--动画

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  6. android动画介绍之 自己定义Animation动画实现qq抖一抖效果

    昨天我们介绍了Animation的基本使用方法.小伙伴们了解的怎么样了?假设还没有了解过Animation的小伙伴能够看看这篇博客 android动画介绍--Animation 实现loading动画 ...

  7. python文件和文件夹訪问File and Directory Access

    http://blog.csdn.net/pipisorry/article/details/47907589 os.path - Common pathname manipulations 都是和路 ...

  8. jmeter入门系列文章二 版本号介绍

    转载时请标注源自:http://blog.csdn.net/musen518 jmeter版本号公布频率一般为1年,每年会有一个版本号升级 截止2015年底,最新版本号为2.13,最新最全的更新信息一 ...

  9. 自己定义View Controller转换动画

    原文链接 : Introduction to Custom View Controller Transitions and Animations 原文作者 : joyce echessa 译文出自 : ...

  10. 个人微信接入图灵机器人(基于Python)

    编程语言:Python2.7 1.在图灵机器人官网(http://www.tuling123.com)注册账号, 创建机器人, 如果只是学习的话,可以使用2免费版 , 一个账号最多可以创建5个机器人 ...