Resumen

Proxy y Reflect son API introducidas por ES6 para operar con objetos.

Proxy puedeSobre目marcaobjetodeLeer取、FunciónLa llamadaesperaroperacióndecimalesLínea拦截, entoncesdecimalesLíneaoperaciónmanera.elloNodirectoConectaroperaciónobjeto, mientras queSícomoProxyEl patrón,Medianteobjetodepares de sustitutos象decimalesLíneaoperación,EndecimalesLíneaestosoperacióncuandopuedeañadiruna些requierede额外operaciones. → Proxy puede interceptar operaciones como la lectura y la invocación de funciones en el objeto objetivo, y luego realizar el procesamiento de la operación. No opera directamente sobre el objeto, sino que, como el patrón proxy, opera a través de un objeto proxy del objeto; al realizar estas operaciones, se pueden añadir algunas operaciones adicionales necesarias.

Reflect puedese utiliza para获取目marcaobjetodecomportamiento, elloy Object similar,pero更fácilLeer,ParaoperaciónobjetoProporcionayauna种更Excelente雅de la siguiente. Estode los métodosy Proxy es correctoenviarde. → Reflect se puede utilizar para obtener el comportamiento del objeto objetivo. Es similar a Object, pero más legible, y proporciona una forma más elegante de operar con objetos. Sus métodos se corresponden con los de Proxy.


Uso básico

Proxy

un Proxy objetoRazóndosunidadsección.grupoCheng: target 、 handler 。EnMediante Proxy estructura造Función生ChengEjemplosobjetocuandorequiereProporcionaestodosunidadel parámetro. target es decir目marcaobjeto, handler Es unobjeto,DeclaraciónyaProxy target de指elcomportamiento. → Un objeto Proxy consta de dos partes: target y handler. Al generar un objeto de instancia mediante el constructor de Proxy, se deben proporcionar estos dos parámetros. target es el objeto objetivo, y handler es un objeto que declara el comportamiento específico del proxy de target.

let target = { name: 'Tom', age: 24 } let handler = { get: function(target, key) { console.log('getting '+key); return target[key]; //No es target.key → No es target.key }, set: function(target, key, value) { console.log('setting '+key); target[key] = value; } } let proxy = new Proxy(target, handler) proxy.name //En realidad se ejecuta handler.get proxy.age = 25 //En realidad se ejecuta handler.set // getting name // setting age // 25 //target puede ser un objeto vacío let targetEpt = {} let proxyEpt = new Proxy(targetEpt, handler) //Se llama al método get; en este momento el objeto objetivo está vacío y no tiene la propiedad name. proxyEpt.name // getting name //Se llama al método set, añadiendo la propiedad name al objeto objetivo. proxyEpt.name = 'Tom' // setting name // "Tom" //Se vuelve a llamar a get; ahora la propiedad name ya existe. proxyEpt.name // getting name // "Tom" //Al crear una nueva instancia mediante el constructor, en realidad se realiza una copia superficial del objeto objetivo, por lo tanto el objeto objetivo y el objeto proxy se influirán mutuamente. //Influencia → mutuamente targetEpt // {name: "Tom"} //El objeto handler también puede estar vacío, lo que equivale a no establecer operaciones de interceptación y acceder directamente al objeto objetivo. let targetEmpty = {} let proxyEmpty = new Proxy(targetEmpty,{}) proxyEmpty.name = "Tom" targetEmpty // {name: "Tom"}

Métodos de instancia → Métodos de instancia

get(target, propKey, receiver)

Se utiliza para la operación de lectura de propKey en el objeto target.

let exam ={ name: "Tom", age: 24 } let proxy = new Proxy(exam, { get(target, propKey, receiver) { console.log('Getting ' + propKey); return target[propKey]; } }) proxy.name // Getting name // "Tom"

El método get() puede heredarse.

let proxy = new Proxy({}, { get(target, propKey, receiver) { //Implementa la protección de lectura de propiedades privadas. if(propKey[0] === '_'){ throw new Erro(`Invalid attempt to get private "${propKey}"`); } console.log('Getting ' + propKey); return target[propKey]; } }); let obj = Object.create(proxy); obj.name // Getting name
set(target, propKey, value, receiver)

Se utiliza para interceptar la operación de asignación de propKey en el objeto target. Si una propiedad propia del objeto target no es escribible ni configurable, entonces el método set no tendrá efecto.

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'); } } //Para la propiedad age y otras propiedades que cumplan las condiciones, se guardan directamente. obj[prop] = value; } }; let proxy= new Proxy({}, validator) proxy.age = 100; proxy.age // 100 proxy.age = 'oppps' //Error → Error proxy.age = 300 //Error → Error

El cuarto parámetro receiver representa el objeto donde se encuentra el comportamiento de la operación original, generalmente la propia instancia de Proxy.

const handler = { set: function(obj, prop, value, receiver) { obj[prop] = receiver; } }; const proxy = new Proxy({}, handler); proxy.name= 'Tom'; proxy.name=== proxy // true const exam = {} Object.setPrototypeOf(exam, proxy) exam.name = "Tom" exam.name === exam // true

Nota: en modo estricto, si el proxy set no devuelve true, se producirá un error.

apply(target, ctx, args)

se utiliza para拦截FuncióndeLa llamada、call y reply operaciones.target representa目marcaobjeto,ctx representa目marcaobjetoarribaabajo,args representa目marcaobjetodeParámetrosmatriz.。 → Se utiliza para interceptar las operaciones de invocación de funciones, call y reply. target representa el objeto objetivo, ctx representa el contexto del objeto objetivo y args representa la matriz de argumentos del objeto objetivo.

function sub(a, b){ return a - b; } let handler = { apply: function(target, ctx, args){ console.log('handle apply'); return Reflect.apply(...arguments); } } let proxy = new Proxy(sub, handler) proxy(2, 1) // handle apply // 1
has(target, propKey)

se utiliza para拦截 HasProperty operación,es decirEn判cortar target objetosiexiste propKey AtributocuandopuedeEdredónEsteMétodo拦截。EsteMétodoNo判cortarunAtributoes correcto象自身deAtributo, tambiénSí继承deAtributo. → Se utiliza para interceptar la operación HasProperty, es decir, cuando se determina si la propiedad propKey existe en el objeto target, será interceptada por este método. Este método no distingue si una propiedad es propia del objeto o heredada.

let handler = { has: function(target, propKey){ console.log("handle has"); return propKey in target; } } let exam = {name: "Tom"} let proxy = new Proxy(exam, handler) 'name' in proxy // handle has // true

Nota: este método no intercepta el bucle for ... in.

construct(target, args)

Se utiliza para interceptar el comando new. El valor de retorno debe ser un objeto.

let handler = { construct: function (target, args, newTarget) { console.log('handle construct') return Reflect.construct(target, args, newTarget) } } class Exam { constructor (name) { this.name = name } } let ExamProxy = new Proxy(Exam, handler) let proxyObj = new ExamProxy('Tom') console.log(proxyObj) // handle construct // exam {name: "Tom"}
deleteProperty(target, propKey)

Se utiliza para interceptar la operación delete. Si este método lanza un error o devuelve false, la propiedad propKey no podrá ser eliminada por el comando delete.

defineProperty(target, propKey, propDesc)

se utiliza para拦截 Object.defineProsi目marcaobjetoNovarExtensión,Agregar目marcaobjetoarribaNoexistedeAtributopuedemensaje错;siAtributoNovarEscribiroNovarconfiguración,entoncesno puedeModificarcambiarestosAtributo. → Se utiliza para interceptar Object.definePro. Si el objeto objetivo no es extensible, añadir una propiedad que no existe en el objeto objetivo provocará un error; si la propiedad no es escribible ni configurable, no se pueden cambiar estas propiedades.

let handler = { defineProperty: function(target, propKey, propDesc){ console.log("handle defineProperty"); return true; } } let target = {} let proxy = new Proxy(target, handler) proxy.name = "Tom" // handle defineProperty target // {name: "Tom"} //Si defineProperty devuelve false, la operación de añadir propiedades no es válida. let handler1 = { defineProperty: function(target, propKey, propDesc){ console.log("handle defineProperty"); return false; } } let target1 = {} let proxy1 = new Proxy(target1, handler1) proxy1.name = "Jerry" target1 // {}

erty operación → erty operación

getOwnPropertyDescriptor(target, propKey)

Se utiliza para interceptar Object.getOwnPropertyD(). El valor de retorno es un objeto de descripción de propiedad o undefined.

let handler = { getOwnPropertyDescriptor: function(target, propKey){ return Object.getOwnPropertyDescriptor(target, propKey); } } let target = {name: "Tom"} let proxy = new Proxy(target, handler) Object.getOwnPropertyDescriptor(proxy, 'name') // {value: "Tom", writable: true, enumerable: true, configurable: // true}

propiedad ptor

getPrototypeOf(target)

Se utiliza principalmente para interceptar operaciones de obtención del prototipo de un objeto. Incluye las siguientes operaciones:

- Object.prototype._proto_
- Object.prototype.isPrototypeOf()
- Object.getPrototypeOf()
- Reflect.getPrototypeOf()
- instanceof
let exam = {} let proxy = new Proxy({},{ getPrototypeOf: function(target){ return exam; } }) Object.getPrototypeOf(proxy) // {}

Nota,Valor de retornodebees correcto象o null , de lo contrariomensaje错。Otro外, si目marcaobjetoNovarExtensión(non-extensible),getPrototypeOf MétododebeDevuelve目marcaobjetodeOriginalpaqueteobjeto. → Nota: el valor de retorno debe ser un objeto o null; de lo contrario, se produce un error. Además, si el objeto objetivo no es extensible, el método getPrototypeOf debe devolver el objeto prototipo del objeto objetivo.

let proxy = new Proxy({},{ getPrototypeOf: function(target){ return true; } }) Object.getPrototypeOf(proxy) // TypeError: 'getPrototypeOf' on proxy: trap returned neither object // nor null
isExtensible(target)

Se utiliza para interceptar la operación Object.isExtensible.

Este método solo puede devolver un valor booleano; de lo contrario, el valor de retorno se convertirá automáticamente a booleano.

let proxy = new Proxy({},{ isExtensible:function(target){ return true; } }) Object.isExtensible(proxy) // true

Nota: su valor de retorno debe ser coherente con la propiedad isExtensible del objeto objetivo; de lo contrario, se lanzará un error.

let proxy = new Proxy({},{ isExtensible:function(target){ return false; } }) Object.isExtensible(proxy) // TypeError: 'isExtensible' on proxy: trap result does not reflect // extensibility of proxy target (which is 'true')
ownKeys(target)

Se utiliza para interceptar operaciones de lectura de las propiedades propias del objeto. Incluye principalmente las siguientes operaciones:

- Object.getOwnPropertyNames()
- Object.getOwnPropertySymbols()
- Object.keys()
- or...in

Los miembros de la matriz devuelta por el método solo pueden ser cadenas o valores Symbol; de lo contrario, se producirá un error.

Si el objeto objetivo contiene propiedades no configurables, estas propiedades deben devolverse en el resultado; de lo contrario, se producirá un error.

Si el objeto objetivo no es extensible, se deben devolver todas y solo las propiedades que contiene el objeto objetivo; no se pueden incluir propiedades inexistentes; de lo contrario, también se producirá un error.

let proxy = new Proxy( { name: "Tom", age: 24 }, { ownKeys(target) { return ['name']; } }); Object.keys(proxy) //[ 'name' ]f En el resultado devuelto, se filtran tres tipos de atributos: //- Propiedades que no existen en el objeto objetivo //- Propiedades cuyo nombre es un valor Symbol //- Propiedades no enumerables let target = { name: "Tom", [Symbol.for('age')]: 24, }; //Añadir la propiedad no enumerable 'gender' Object.defineProperty(target, 'gender', { enumerable: false, configurable: true, writable: true, value: 'male' }); let handler = { ownKeys(target) { return ['name', 'parent', Symbol.for('age'), 'gender']; } }; let proxy = new Proxy(target, handler); Object.keys(proxy) // ['name']
preventExtensions(target)

Interceptar la operación Object.preventExtensions.

Este método debe devolver un valor booleano; de lo contrario, se convertirá automáticamente a booleano.

//Solo cuando el objeto objetivo no es extensible (es decir, Object.isExtensible(proxy) es false), //proxy.preventExtensions puede devolver true; de lo contrario, se producirá un error. var proxy = new Proxy({}, { preventExtensions: function(target) { return true; } }); //Dado que proxy.preventExtensions devuelve true, aquí también devolverá true, por lo que se producirá un error. Object.preventExtensions(proxy)Edredón// TypeError: 'preventExtensions' on proxy: trap returned truish but // the proxy target is extensible //Solución → Solución var proxy = new Proxy({}, { preventExtensions: function(target) { //Antes de devolver, llamar primero a Object.preventExtensions Object.preventExtensions(target); return true; } }); Object.preventExtensions(proxy) // Proxy {}
setPrototypeOf

Se utiliza principalmente para interceptar el método Object.setPrototypeOf.

El valor de retorno debe ser booleano; de lo contrario, se convertirá automáticamente a booleano.

Si el objeto objetivo no es extensible, el método setPrototypeOf no debe cambiar el prototipo del objeto objetivo.

let proto = {} let proxy = new Proxy(function () {}, { setPrototypeOf: function(target, proto) { console.log("setPrototypeOf"); return true; } } ); Object.setPrototypeOf(proxy, proto); // setPrototypeOf
Proxy.revocable()

Se utiliza para devolver una instancia de Proxy cancelable.

let {proxy, revoke} = Proxy.revocable({}, {}); proxy.name = "Tom"; revoke(); proxy.name // TypeError: Cannot perform 'get' on a proxy that has been revoked

Reflect

ES6 Mediova a Object deuna些明显Atributoqueque en el lenguajedentro部de los métodosmover植llegarya Reflect objetoarriba(actualmenteAlgunosMétodopuedeal mismo tiempoexisteque Object y Reflect objetoarriba),未venirdeNuevoMétodopuede只部署En Reflect objetoArriba. → En ES6, algunos métodos de Object que claramente pertenecen a la parte interna del lenguaje se trasladaron al objeto Reflect (actualmente algunos métodos existen tanto en Object como en Reflect), y los nuevos métodos futuros se implementarán solo en el objeto Reflect.

El objeto Reflect ha modificado los resultados de retorno de algunos métodos para hacerlos más razonables.

El objeto Reflect implementa las operaciones imperativas de Object mediante funciones.

Métodos estáticos

Reflect.get(target, name, receiver)

Busca y devuelve la propiedad name del objeto target.

let exam = { name: "Tom", age: 24, get info(){ return this.name + this.age; } } Reflect.get(exam, 'name'); // "Tom" //Cuando en el objeto target existe un método getter para la propiedad name, el this del método getter se vinculará a // receiver let receiver = { name: "Jerry", age: 20 } Reflect.get(exam, 'info', receiver); // Jerry20 //Cuando name es una propiedad que no existe en el objeto target, devuelve undefined. Reflect.get(exam, 'birth'); // undefined //Cuando target no es un objeto, se producirá un error. Reflect.get(1, 'name'); // TypeError
Reflect.set(target, name, value, receiver)

Establece la propiedad name de target como value. El valor de retorno es boolean; true indica que la modificación fue exitosa, false indica fallo. Cuando target es un objeto inexistente, se producirá un error.

let exam = { name: "Tom", age: 24, set info(value){ return this.age = value; } } exam.age; // 24 Reflect.set(exam, 'age', 25); // true exam.age; // 25 //Cuando value está vacío, la propiedad name se eliminará. Reflect.set(exam, 'age', ); // true exam.age; // undefined //Cuando en el objeto target existe un método setter para la propiedad name, el this dentro del método setter se vinculará a // receiver, por lo que lo que se modifica en realidad es la propiedad de receiver, let receiver = { age: 18 } Reflect.set(exam, 'info', 1, receiver); // true receiver.age; // 1 let receiver1 = { name: 'oppps' } Reflect.set(exam, 'info', 1, receiver1); receiver1.age; // 1
Reflect.has(obj, name)

Sí name in obj DirectivadeFunciónTransformación,se utiliza paraConsultar找 name AtributoEn obj objetoMediosiexiste. Devuelvees boolean. Si obj no esobjetoentoncespuedemensaje错 TypeError. → Es la versión funcional de la instrucción name in obj; se utiliza para comprobar si la propiedad name existe en el objeto obj. El valor de retorno es boolean. Si obj no es un objeto, se lanzará un error TypeError.

let exam = { name: "Tom", age: 24 } Reflect.has(exam, 'name'); // true
Reflect.deleteProperty(obj, property)

Es la versión funcional de delete obj[property]; se utiliza para eliminar la propiedad property del objeto obj. El valor de retorno es boolean. Si obj no es un objeto, se lanzará un error TypeError.

let exam = { name: "Tom", age: 24 } Reflect.deleteProperty(exam , 'name'); // true exam // {age: 24} //Cuando property no existe, también devolverá true. Reflect.deleteProperty(exam , 'name'); // true
Reflect.construct(obj, args)

Equivale a new target(...args). → Equivale a new target(...args).

function exam(name){ this.name = name; } Reflect.construct(exam, ['Tom']); // exam {name: "Tom"}
Reflect.getPrototypeOf(obj)

Se utiliza para leer la propiedad _proto_ de obj. Cuando obj no es un objeto, a diferencia de Object, no convertirá obj en objeto, sino que se producirá un error.

class Exam{} let obj = new Exam() Reflect.getPrototypeOf(obj) === Exam.prototype // true
Reflect.setPrototypeOf(obj, newProto)

Se utiliza para establecer el prototype del objeto objetivo.

let obj ={} Reflect.setPrototypeOf(obj, Array.prototype); // true
Reflect.apply(func, thisArg, args)

Equivalente a Function.prototype.apply.call(func, thisArg, args). func representa la función objetivo; thisArg representa el objeto this al que está vinculada la función objetivo; args representa la lista de parámetros pasados al llamar a la función objetivo, que puede ser un array o un objeto similar a un array. Si la función objetivo no se puede invocar, se lanzará un TypeError.

Reflect.apply(Math.max, Math, [1, 3, 5, 3, 1]); // 5
Reflect.defineProperty(target, propertyKey, attributes)

Se utiliza para definir propiedades en el objeto objetivo. Si target no es un objeto, se lanzará un error.

let myDate= {} Reflect.defineProperty(MyDate, 'now', { value: () => Date.now() }); // true const student = {}; Reflect.defineProperty(student, "name", {value: "Mike"}); // true student.name; // "Mike"
Reflect.getOwnPropertyDescriptor(target, propertyKey)

Se utiliza para obtener el objeto descriptor de la propiedad propertyKey del objeto target. Cuando target no es un objeto, se lanzará un error indicando que el parámetro no es válido, y no se convertirá el no-objeto en un objeto.

var exam = {} Reflect.defineProperty(exam, 'name', { value: true, enumerable: false, }) Reflect.getOwnPropertyDescriptor(exam, 'name') // { configurable: false, enumerable: false, value: true, writable: // false} //Cuando la propiedad propertyKey no existe en el objeto target, devuelve undefined. Reflect.getOwnPropertyDescriptor(exam, 'age') // undefined
Reflect.isExtensible(target)

Se utiliza para determinar si el objeto target es extensible. El valor de retorno es boolean. Si el parámetro target no es un objeto, se lanzará un error.

let exam = {} Reflect.isExtensible(exam) // true
Reflect.preventExtensions(target)

Se utiliza para hacer que el objeto target sea no extensible. Si el parámetro target no es un objeto, se lanzará un error.

let exam = {} Reflect.preventExtensions(exam) // true
Reflect.ownKeys(target)

Se utiliza para devolver todas las propiedades del objeto target, equivalente a la suma de Object.getOwnPropertyNames y Object.getOwnPropertySymbols.

var exam = { name: 1, [Symbol.for('age')]: 4 } Reflect.ownKeys(exam) // ["name", Symbol(age)]

Uso combinado

Los métodos del objeto Reflect se corresponden uno a uno con los métodos del objeto Proxy. Por lo tanto, los métodos del objeto Proxy pueden obtener el comportamiento predeterminado llamando a los métodos del objeto Reflect y luego realizar operaciones adicionales.

let exam = { name: "Tom", age: 24 } let handler = { get: function(target, key){ console.log("getting "+key); return Reflect.get(target,key); }, set: function(target, key, value){ console.log("setting "+key+" to "+value) Reflect.set(target, key, value); } } let proxy = new Proxy(exam, handler) proxy.name = "Jerry" proxy.name // setting name to Jerry // getting name // "Jerry"

Ampliación de escenarios de uso

Implementar el patrón observador

//Definir una colección Set const queuedObservers = new Set(); //Colocar todas las funciones observadoras en la colección Set const observe = fn => queuedObservers.add(fn); //observable devuelve un proxy del objeto original e intercepta las operaciones de asignación const observable = obj => new Proxy(obj, {set}); function set(target, key, value, receiver) { //Obtener la operación de asignación del objeto const result = Reflect.set(target, key, value, receiver); //Ejecutar todos los observadores queuedObservers.forEach(observer => observer()); //Ejecutar la operación de asignación return result; }