ES6学习笔记: 代理和反射
前端时间学习Vue的时候,碰到Proxy,当时就一脸蒙逼了。所以返过头来补一下相关的知识。在JavaScript中有Proxy和Reflect的两个概念。最近几天一直在学习这两个概念,今天整整这方面的相关知识点。
术语介绍
Proxy又称为代理。在现实生活中,大家对代理二字并不会太陌生,比如某产品的代理。打个比方来说,我们要买一台手机,我们不会直接到一家手机厂去买,会在手机的代理商中买。
在JavaScript中,Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种元编程,即对编程语言进行编程。
Proxy可以理解成,在目标对象之前架设一层拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来代理某些操作,可以译为代理器。
Reflect称为反射。它也是ES6中为了操作对象而提供的新的API,用来替代直接调用Object的方法。Reflect是一个内置的对象,它提供可拦截JavaScript操作的方法。方法与代理处理程序的方法相同。Reflect不是一个函数对象,因此它是不可构造的。
Reflect与大多数全局对象不同,Reflect没有构造函数。你不能将其与一个new运算符一起使用,或者将Reflect对象作为一个函数来调用。
简单的看一个小示例:
let target = {}
let handler = {
get: function (target, key, receiver) {
console.log(`Getting ${key}!`)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
console.log(`Setting ${key}!`)
return Reflect.set(target, key, value, receiver)
}
}
let proxy = new Proxy(target, handler)
console.log(proxy.name)
proxy.name = '大漠'
console.log(proxy.name)
proxy.count = 1
proxy.count++
console.log(proxy.count)
运行的结果如下:

上面代码对一个空对象架设了一层拦截,重新定义了属性的读取(get)和设置(set)行为。这里暂时先不解释具体的语法,只看运行结果。
在上面的示例中,我们看到了target、handler和trap。简单对介绍一下:
target代表了被代理的对象。这是你需要控制对其访问的对象。它始终作为Proxy构造器的第一个参数被传入,同时它也会被传入每个trap。handler是一个包含了你想要拦截和处理的操作的对象。它会被作为Proxy构造器的第二个参数传入。它实现了Proxy API(比如:get,set,apply等等)。- 一个
trap代表了handler中一个被处理的函数。因此,如果要拦截get请求你需要创建一个get的trap。以此类推。
基本语法
前面我们了解了相关的术语,我们来看看他们的基本语法。首先来看看Proxy的基本语法格式:
// @param {Object} target 用来被代理的对象
// @param {Object} handler 用来设置代理的对象
let proxy = new Proxy(target, handler)
ES6原生提供Proxy构造函数,用来生成Proxy实例。在使用过程当中,Proxy都像上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
let proxy = new Proxy({}, {
get: function(target, property) {
return property in target ? target[property] : `Error: ${target} object not has ${property} property! `
}
})
console.log(proxy.time)
console.log(proxy.name)
console.log(proxy.title)
proxy.time = new Date()
proxy.name = '大漠'
proxy.title = '切图仔'
console.log(proxy.time)
console.log(proxy.name)
console.log(proxy.title)

上面代码中,作来构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(一个空对象{}),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代码的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面的代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是target(目标对象)和property(所要访问的属性)。
注意,要使得
Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
Reflect只是一个内置的对象,它提供可拦截JavaScript操作的方法。方法与代理处理程序的方法相同(稍后会介绍)。Reflect不是一个函数对象,因此它是不可构造的。
new Reflect() // 错误的写法
Reflect提供了一些静态方法,静态方法是指只能通过对象自身访问的方法:

处理器对象
处理器对象handler一共提供了14种可代理操作,每种操作的代号(属性名/方法名)和触发这种操作的方式如下:
handler.getPrototypeOf():在读取代理对象的原型时触发该操作,比如在执行Object.getPrototypeOf(proxy)时handler.setPrototypeOf():在设置代理对象的原型时触发该操作,比如在执行Object.setprototypeOf(proxy, null)时handler.isExtensible():在判断一个代理对象是否是可扩展时触发该操作,比如在执行Object.isExtensible(proxy)时handler.preventExtensions():在让一个代理对象不可扩展时触发该操作,比如在执行Object.preventExtensions(proxy)时handler.getOwnPropertyDescriptor():在获取代理对象某个属性的属性描述时触发该操作,比如在执行Object.getOwnPropertyDescriptor(proxy, 'foo')时handler.defineProperty():在定义代理对象某个属性时的属性描述时触发该操作,比如在执行Object.defineProperty(proxy,'foo',{})时handler.has():在判断代理对象是否拥有某个属性时触发该操作,比如在执行'foo' in proxy时handler.get():在读取代理对象的某个属性时触发该操作,比如在执行proxy.foo时handler.set():在给代理对象的某个赋值时触发该操作,比如在执行proxy.foo = 1时handler.deleteProperty():在删除代理对象的某个属性时触发该操作,比如在执行delete proxy.foo时handler.ownKeys():在获取代理对象的所有属性键时触发该操作,比如在执行Object.getOwnPropertyNames(proxy)时handler.apply():在调用一个目标对象为函数的代理对象时触发该操作,比如在执行proxy()时handler.construct():在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy()时
Reflect对象拥有对应的可以控制各种元编程任务的静态方法。这些功能和Proxy一一对应。
下面的这些名称你可能看起来很眼熟(因为他们也是Object上的方法):
Reflect.getOwnPropertyDescriptor(..)Reflect.defineProperty(..)Reflect.getPrototypeOf(..)Reflect.setPrototypeOf(..)Reflect.preventExtensions(..)Reflect.isExtensible(..)
这些方法和在Object上的同名方法一样。然后,一个区别在于,Object上这么方法的第一个参数是一个对象,Reflect遇到这种情况会扔出一个错误。
如果handler没有设置任何拦截,那就等同于直接通向原对象。
let target = {}
let handler = {}
let proxy = new Proxy(target, handler)
proxy.name = '大漠'
console.log(`Proxy: ${proxy.name}`) // => Proxy: 大漠
console.log(`Target: ${target.name}`) // => Target: 大漠
上面代码中,handler是一个空对象,没有任何拦截效果,访问handler就等同于访问target。
Proxy支持的拦截操作
handler.get()
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']、Reflect.get(...),返回类型不限。最后一个参数receiver可选,当target对象设置了propKey属性的get函数时,receiver对象会绑定get函数的this对象
let person = {
name: '大漠'
}
let handler = {
get: function (target, propKey, receiver) {
if (propKey in target) {
return target[propKey]
} else {
throw new ReferenceError(`Property ${propKey} does not exist!`)
}
}
}
let proxy = new Proxy(person, handler)
console.log(proxy.name)
console.log(proxy.age)

上面的代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
get()方法是可以继承的。
let proxy = new Proxy({},{
get(target, propKey, receiver) {
console.log(`GET: ${propKey}`)
return target[propKey]
}
})
let obj = Object.create(proxy)
console.log(obj.name)

上面代码中,拦截操作定义在Prototype对象上面,所以如果读取obj对象继承的属性时,拦截会生效。
下面的例子,使用get拦截,实现数组读取负数的索引。
function createArray(...items) {
let handler = {
get(target, propKey, receiver) {
let index = parseInt(propKey)
if (index < 0) {
propKey = String(target.length + index)
}
return Reflect.get(target, propKey, receiver)
}
}
let target = []
target.push(...items)
return new Proxy(target, handler)
}
let array = createArray('大漠', 'w3cplus.com', '切图仔')
console.log(array[-1]) // => 切图仔
上面代码中,数组的位置参数是-1,就会输出数组的倒数第一个item值,也就是切图仔。
handler.set()
set(target, propKey, value, receiver)用来拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v、Reflect.set(...),返回一个布尔值。
假定Person对象有一个age属性,该属性应该是一个不大于100的整数,那么可以使用Proxy对象保证age的属性值符合要求。
let validator = {
set: function (target, propKey, value, receiver) {
if (propKey === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('age不是一个整数')
}
if (value > 100) {
throw new RangeError('age不是有效值')
}
}
// 对于age以外的属性,直接保存
target[propKey] = value
}
}
let Person = new Proxy({}, validator)
Person.age = 36
Person.name = '大漠'
console.log(Person.age) // => 36
console.log(Person.name) // => 大漠
Person.age = 200
console.log(Person.age) // => age不是有效值

上面的示例中,由于设置了存值函数set(),任何不符合要求的age属性赋值,都会抛出一个错误。利用set()方法,还可以数据绑定,即每当对象发生变化时,会自动更新。在使用JavaScript对表单进行验证时,非常有用。
利用proxy拦截不符合要求的数据
function validator(target, validator, errorMsg) {
return new Proxy(target, {
_validator: validator,
set(target, key, value, proxy) {
let errMsg = errorMsg
if (value == '') {
alert(`${errMsg[key]}不能为空!`)
return target[key] = false
}
let va = this._validator[key]
if (!!va(value)) {
return Reflect.set(target, key, value, proxy)
} else {
alert(`${errMsg[key]}格式不正确`)
return target[key] = false
}
}
})
}
负责校验的逻辑代码
const validators = {
name(value) {
return value.length > 6
},
passwd(value) {
return value.length > 6
},
moblie(value) {
return /^1(3|5|7|8|9)[0-9]{9}$/.test(value)
},
email(value) {
return /^\w+([+-.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value)
}
}
客户端调用代码
const errorMsg = {
name: '用户名',
passwd: '密码',
moblie: '手机号码',
email: '邮箱地址'
}
const vali = validator({}, validators, errorMsg)
let registerForm = document.querySelector('#registerForm')
registerForm.addEventListener(
'submit',
function() {
let validatorNext = function*() {
yield vali.name = registerForm.userName.value
yield vali.passwd = registerForm.passWord.value
yield vali.moblie = registerForm.phoneNumber.value
yield vali.email = registerForm.emailAddress.value
}
let validator = validatorNext()
validator.next();
!vali.name || validator.next(); //上一步的校验通过才执行下一步
!vali.passwd || validator.next();
!vali.moblie || validator.next();
},
false)
上面这部分验证表单的代码来自于@jawil的《探索两种优雅的表单验证》
handler.has()
has(target, propKey)用来拦截propKey in proxy、Reflect.has(...)的操作,返回一个布尔值。
has()方法可以隐藏某些属性,不被in操作符发现。
let handler = {
has: function(target, key) {
if (key[0] === '_') {
return false
}
return key in target
}
}
let target = {
_prop: '大漠',
prop: 'W3cplus'
}
let proxy = new Proxy(target, handler)
console.log('_prop' in proxy) // => false
console.log('prop' in proxy) // => true
如果原对象的属性名的第一个字符是下划线,proxy.has就会返回false,从而不会被in运算符发现。如果原对象不可配置或者禁止扩展,这个时候,has()拦截就会报错。
let target = {
age: 30,
name: '大漠'
}
Object.preventExtensions(target)
let handler = {
has: function (target, propKey) {
return false
}
}
let proxy = new Proxy(target, handler)
console.log(proxy.age)
console.log(proxy.name)
console.log('age' in proxy)

上例中,target对象禁止扩展,结果使用has()拦截就会报错。
handler.construct()
construct(target, args, proxy)用来拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)和Reflect.construct(...)。
construct()方法用来拦截new命令。
let handler = {
construct: function (target, args) {
console.log('Called:' + args.join(','))
return {value: args[0] * 10}
}
}
let target = function (){}
let proxy = new Proxy(target, handler)
console.log(new proxy(2).value)

如果construct()方法返回的不是对象,就会抛出错误。
let handler = {
construct: function (target, args) {
return 'w3cplus'
}
}
let target = function () {}
let proxy = new Proxy(target, handler)
console.log(new proxy())

handler.deleteProperty()
deleteProperty(target, propKey)用于拦截delete proxy[propKey]和Reflect.deleteProperty(...)的操作,返回一个布尔值。如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
let target = {
_prop: '大漠',
prop: 'w3cplus'
}
let handler = {
deleteProperty: function (target, propKey) {
invariant(propKey, 'delete')
return true
}
}
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private '${key}' property!`)
}
}
let proxy = new Proxy(target, handler)
console.log(delete proxy.prop)
console.log(delete proxy._prop)

上面示例中,deleteProperty()方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。
handler.defineProperty()
defineProperty(target, propKey, propDesc)用于拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDesc)、Reflect.defineProperty(...),返回一个布尔值。
let target = {}
let handler = {
defineProperty: function (target, propKey, propDescriptor) {
console.log(`Called: ${propKey}`)
return false
}
}
let proxy = new Proxy(target, handler)
let propDescriptor = {
configurable: true,
enumerable: true,
value: 10
}
Object.defineProperty(proxy, 'name', propDescriptor)

当调用 Object.defineProperty() 或者 Reflect.defineProperty(),传递给 defineProperty 的 descriptor 有一个限制 - 只有以下属性才有用,非标准的属性将会被无视:
enumerableconfigurablewritablevaluegetset
如果违背了以下的不变量,Proxy会抛出TypeError:
- 如果目标对象
target不可扩展,将不能添加属性 - 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话
- 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的
- 如果一个属性在目标对象中存在对应的属性,那么
Object.defineProperty(target, propKey, propDescriptor)将不会抛出异常 - 在严格模式下,
false作为handler.defineProperty方法的返回值的话将会抛出TypeError异常
handler.getOwnPropertyDescriptor()
getOwnPropertyDescriptor(target, propKey)用于拦截Object.getOwnPropertyDescriptor(proxy, propKey)和Reflect.getOwnPropertyDescriptor(...),返回属性的描述对象或者undefined。
let handler = {
getOwnPropertyDescriptor: function (target, key) {
console.log(`Called: ${key}`)
if (key[0] === '_') {
return
}
return Object.getOwnPropertyDescriptor(target, key)
}
}
let target = {
_prop: '大漠',
prop: 'w3cplus'
}
let proxy = new Proxy(target, handler)
console.log(Object.getOwnPropertyDescriptor(proxy, 'name'))
console.log(Object.getOwnPropertyDescriptor(proxy, '_prop'))
console.log(Object.getOwnPropertyDescriptor(proxy, 'prop'))

如果下列不变量被违反,代理将抛出一个 TypeError:
getOwnPropertyDescriptor必须返回一个object或undefined- 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在。
- 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在
- 如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在
- 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在
Object.getOwnPropertyDescriptor(target)的结果可以使用Object.defineProperty应用于目标对象,也不会抛出异常
以下是 Object.getOwnPropertyDescriptor() 的代码违反了不变量:
let target = {
name: '大漠'
}
Object.preventExtensions(target)
let handler = {
getOwnPropertyDescriptor: function (target, propKey) {
console.log(`Called: ${propKey}`)
return undefined
}
}
let proxy = new Proxy(target, handler)
console.log(Object.getOwnPropertyDescriptor(proxy, 'name'))

handler.getPrototypeOf()
getPrototypeOf(target)用于拦截Object.getPrototypeOf(proxy)、Reflect.preventExtensions(...)、__proto__、Object.isPrototypeOf()、instanceof,返回一个对象。
let target = {}
let handler = {
getPrototypeOf: function (target) {
return Array.prototype
}
}
let proxy = new Proxy(target, handler)
console.log(
`Object.getPrototypeOf(proxy): ${Object.getPrototypeOf(proxy) === Array.prototype},
Reflect.getPrototypeOf(proxy): ${Reflect.getPrototypeOf(proxy) === Array.prototype},
proxy.__proto__: ${proxy.__proto__ === Array.prototype},
Array.prototype.isPrototypeOf(proxy): ${Array.prototype.isPrototypeOf(proxy)},
proxy instanceof Array: ${proxy instanceof Array}`)

如果遇到了下面两种情况,JavaScript 引擎会抛出 TypeError 异常:
getPrototypeOf()方法返回的不是对象也不是null- 目标对象是不可扩展的,且
getPrototypeOf()方法返回的原型不是目标对象本身的原型
下面的示例展示的两种情况下就会报异常:
let proxy1 = new Proxy(
{},
{
getPrototypeOf: function (target) {
return '大漠'
}
}
)
console.log(Object.getPrototypeOf(proxy1))
let proxy2 = new Proxy(
Object.preventExtensions({}),
{
getPrototypeOf: function (target) {
return {}
}
}
)
console.log(Object.getPrototypeOf(proxy2))

handler.isExtensible()
isExtensible(target)拦截Object.isExtensible(proxy)和Reflect.isExtensible(...),返回一个布尔值。
let target = {}
let handler = {
isExtensible: function (target) {
console.log(`Called`)
return true
}
}
let proxy = new Proxy(target, handler)
console.log(Object.isExtensible(proxy))

这个方法有一个强限制,如果不能满足下面的条件,就会抛出错误。
Object.isExtensible(proxy) === Object.isExtensible(target)
下面是一个示例:
let target = {}
let handler = {
isExtensible: function (target) {
console.log('Call')
return false
}
}
let proxy = new Proxy(target, handler)
console.log(Object.isExtensible(proxy))

handler.ownKeys()
ownKeys(target)用于拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、Reflect.get()和Reflect.ownKeys(...),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
let target = {}
let handler = {
ownKeys: function (target) {
console.log('Called')
return ['a', 'b', 'c']
}
}
let proxy = new Proxy(target, handler)
console.log(Object.getOwnPropertyNames(proxy))

如果违反了下面的约定,Proxy将抛出错误 TypeError:
ownKeys的结果必须试一个数组- 数组的元素类型要么是一个
String,要么是一个Symbol - 结果列表必须包含目标对象的所有不可配置(non-configurable )、自有(
own)属性的key - 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(
own)属性的key,不能有其它值
下面的代码违反了约定,将会抛出TypeError:
let target = {}
Object.defineProperty(
target,
'name',
{
configurable: false,
enumerable: true,
value: 10
}
)
let handler = {
ownKeys: function (target) {
return [123, 12.5, true, false, undefined, null, {}, []]
}
}
let proxy = new Proxy(target, handler)
console.log(Object.getOwnPropertyName(proxy))

handler.preventExtensions()
preventExtensions(target)用于拦截Object.preventExtensions(proxy)和Reflect.preventExtensions(...),将返回一个布尔值。这个方法有一个限制,只有当Object.isExtensible(proxy)为false(即不可扩展)时,proxy.preventExtensions才能返回true,否则会报错。
let target = {}
let handler = {
preventExtensions: function(target) {
return true
}
}
let proxy = new Proxy(target, handler)
console.log(Object.preventExtensions(proxy))

上面的代码,proxy.preventExtensions方法返回true,但这时Object.isExtensible(proxy)会返回true,因此报错。为了防止出现这个问题,通常要在proxy.preventExtensions方法里面,调用一次Object.preventExtensions。
let target = {}
let handler = {
preventExtensions: function (target) {
console.log('Called')
Object.preventExtensions(target)
return true
}
}
let proxy = new Proxy (target, handler)
console.log(Object.preventExtensions(proxy))

handler.setPrototypeOf()
setPrototypeOf(target, proto)用于拦截Object.setPrototypeOf(proxy, proto)和Reflect.setPrototypeOf(...)。将返回一个布尔值。
let target = function () {}
let handler = {
setPrototypeOf: function(target, propKey) {
throw new Error('Changing the prototype is forbidden')
}
}
let propKey = {}
let proxy = new Proxy(target, handler)
console.log(proxy.setPrototypeOf(proxy, propKey))

handler.apply()
apply(target, object, args)用于拦截Proxy实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)和Reflect.apply(...)。
let target = function () {}
let handler = {
apply: function (target, thisArg, argumentsList) {
console.log(`Called: ${argumentsList.join(',')}`)
return argumentsList[0] + argumentsList[1] + argumentsList[2]
}
}
let proxy = new Proxy (target, handler)
console.log(proxy(1, 2, 3))

proxy.revocable()
Proxy的revocable的意思是,取消访问proxy 的权限. 这个有什么用呢? 没有什么卵用… 开玩笑的. 他的映射范围应该算是比较少的。但是往往却是精华. 现在,我们有这样一个场景,假如,你现在有一个敏感数据, 但如果突然发生错误,你想立即取消掉对该数据的访问,so? 一般,要不就是直接删除该数据。 但这样方式,太简单粗暴了。 而,proxy提供了一个完美的trick来帮助我们实现这一方式。使用revocable()方法. 这时, 定义一个proxy就需要使用 Proxy.revocable(target, handler)。
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对象的设计目的有这样几个。
- 将
Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。 - 修改某些
Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。 - 让
Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。 Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
一个对象的键们可以这样被访问:
Reflect.ownKeys(..):返回对象自己的键(不是通过“继承”的),同样也返回Object.getOwnPropertyNames()和Object.getOwnPropertySymbols()Reflect.enumerate(..):返回对象上可枚举的属性(包括“继承”过来的)。Reflect.has(..):和in操作符差不多。
函数的调用和构造调用可以手动通过下面 API 执行:
Reflect.apply(..):举个例子,Reflect.apply(foo,thisObj,[42,"bar"])调用了foo()函数,上下文是thisObj,参数为42和bar。Reflect.construct(..):举个例子,Reflect.construct(foo,[42,"bar"])等于new foo(42,"bar")。
对象属性访问,设置,删除可以手动通过下面 API 执行:
Reflect.get():Reflect.get(o,"foo")等于o.foo。Reflect.set():Reflect.set(o,"foo",42)等于o.foo = 42。Reflect.deleteProperty():Reflect.deleteProperty(o,"foo")等于delete o.foo。
Reflect的元编程能力可以让你等效的模拟以前隐藏的各种语法特性。这样你就可以使用这些功能为特定语言(DSL)拓展新功能和 API。
总结
这篇文章主要整理了有关于JavaScript中的Proxy和Reflect相关的知识点。整理的有点混乱,如果你有更好的经验或者说文章中有不对之处,还请在下面的评论中与我一起分享。
如需转载,烦请注明出处:https://www.fedev.cn/javascript/es6-proxy-reflect.htmljordan retro 11 mens sneakers
