javascript - es6 - ¿Cómo probar si un objeto es un Proxy?
javascript proxy get (11)
Me gustaría probar si un objeto JavaScript es un Proxy . El enfoque trivial
if (obj instanceof Proxy) ...
no funciona aquí, ni atraviesa la cadena de prototipos para
Proxy.prototype
, ya que todas las operaciones relevantes están respaldadas efectivamente por el objetivo subyacente.
¿Es posible probar si un objeto arbitrario es un Proxy?
Crea un nuevo símbolo:
let isProxy = Symbol("isProxy")
Dentro del método
get
de su controlador proxy, puede verificar si la
key
es su símbolo y luego
return true
:
get(target, key)
{
if (key === isProxy)
return true;
// normal get handler code here
}
Luego puede verificar si un objeto es uno de sus servidores proxy utilizando el siguiente código:
if (myObject[isProxy]) ...
Creo que he encontrado una forma más segura de verificar si el artículo es un proxy. Esta respuesta fue inspirada por la respuesta de Xabre .
function getProxy(target, property) {
if (property === Symbol.for("__isProxy")) return true;
if (property === Symbol.for("__target")) return target;
return target[property];
}
function setProxy(target, property, value) {
if (property === Symbol.for("__isProxy")) throw new Error("You cannot set the value of ''__isProxy''");
if (property === Symbol.for("__target")) throw new Error("You cannot set the value of ''__target''");
if (target[property !== value]) target[property] = value;
return true;
}
function isProxy(proxy) {
return proxy == null ? false : !!proxy[Symbol.for("__isProxy")];
}
function getTarget(proxy) {
return isProxy(proxy) ? proxy[Symbol.for("__target")] : proxy;
}
function updateProxy(values, property) {
values[property] = new Proxy(getTarget(values[property]), {
set: setProxy,
get: getProxy
});
}
Esencialmente, lo que he hecho es, en lugar de agregar el campo
__isProxy
al destino, agregué esta verificación:
if (property === Symbol.for("__isProxy")) return true;
en el captador del proxy.
De esta manera, si está utilizando un bucle for-in o
Object.keys
o
Object.hasOwnProperty
, __isProxy no existirá.
Desafortunadamente, aunque puede establecer el valor de
__isProxy
, nunca podrá recuperarlo, debido a la verificación en el getter.
Por lo tanto, debe arrojar un error cuando se establece el campo.
También puede usar un
Symbol
para verificar si una variable es un Proxy, si cree que es probable que quiera usar
__isProxy
como una propiedad diferente.
Finalmente, también agregué una funcionalidad similar para el objetivo del proxy, que también puede ser tan difícil de recuperar.
De hecho, existe una solución para determinar si el objeto es proxy, que se basa en varios supuestos.
En primer lugar, la determinación de proxy se puede resolver fácilmente para el entorno
node.js
través de extensiones C ++ o una página web privilegiada en el navegador, cuando la página puede iniciar extensiones no seguras.
En segundo lugar, Proxy es una funcionalidad relativamente nueva, por lo que no existe en los navegadores antiguos, por lo que la solución solo funciona en los navegadores modernos.
El motor JS no puede clonar funciones (ya que tienen enlaces al contexto de activación y algunas otras razones), pero el objeto Proxy por definición consiste en controladores de envoltura. Entonces, para determinar si el objeto es proxy, es suficiente para iniciar la clonación de objetos forzados. Se puede hacer a través de la función postMessage .
Si el objeto es Proxy, no se podrá copiar aunque no contenga ninguna función.
Por ejemplo, Edge y Chrome producen los siguientes errores al intentar publicar el objeto Proxy:
[object DOMException]: {code: 25, message: "DataCloneError", name: "DataCloneError"}
y no se
Failed to execute ''postMessage'' on ''Window'': [object Object] could not be cloned.
.
Desde http://www.2ality.com/2014/12/es6-proxies.html :
Es imposible determinar si un objeto es un proxy o no (virtualización transparente).
El mejor método que he encontrado es crear un conjunto débil de objetos proxy. Puede hacer esto de forma recursiva cuando construye y verifica sus objetos proxy.
var myProxySet = new WeakSet();
var myObj = new Proxy({},myValidator);
myProxySet.add(myObj);
if(myProxySet.has(myObj)) {
// Working with a proxy object.
}
En Node.js 10 puede usar
util.types.isProxy
.
Por ejemplo:
const target = {};
const proxy = new Proxy(target, {});
util.types.isProxy(target); // Returns false
util.types.isProxy(proxy); // Returns true
En mi proyecto actual también necesitaba una forma de definir si algo ya era un Proxy, principalmente porque no quería iniciar un proxy en un proxy. Para esto, simplemente agregué un captador a mi controlador, que devolvería verdadero si la variable solicitada fuera "__Proxy":
function _observe(obj) {
if (obj.__isProxy === undefined) {
var ret = new Proxy(obj || {}, {
set: (target, key, value) => {
/// act on the change
return true;
},
get: (target, key) => {
if (key !== "__isProxy") {
return target[key];
}
return true;
}
});
return ret;
}
return obj;
}
Puede que no sea la mejor solución, pero creo que es una solución elegante, que tampoco aparece cuando se serializa.
Es imposible detectar si algo es un
Proxy
según la especificación del lenguaje JS.
El nodo proporciona un mecanismo a través del código nativo, pero no recomiendo su uso; no se supone que sepa si algo es un
Proxy
.
Otras respuestas que sugieren envolver o sombrear el
Proxy
global en realidad no funcionarán entre reinos (es decir, iframes, trabajadores web, módulo vm de nodo, wasm, etc.).
Hay dos formas de representar un objeto.
Uno es
new Proxy
, otro es
Proxy.revocable
.
Podemos espiarlos para que el objeto proxy se registre en una lista secreta.
Luego determinamos que un objeto es un objeto proxy comprobando si existe en la lista secreta.
Para espiar funciones, podemos escribir envoltorios o usar el Proxy incorporado.
Esto último significa que use Proxy para proxy
new Proxy
así como
Proxy.recovable
, aquí hay un
fiddle
para demostrar la idea.
Para servir la
antigua API Proxy
como nodejs-v5.8.0 Proxy, podemos aplicar la misma idea usando
Proxy.createFunction
para proxy
Proxy.create
y
Proxy.createFunction
.
Matthew Brichacek y David Callanan dan buenas respuestas para el Proxy que creas tú mismo, pero si no es el caso, aquí hay algunas adiciones
Imagine que tiene una función externa que crea Proxy que no puede modificar
const external_script = ()=>{
return new Proxy({a:5},{})
}
Antes de cualquier ejecución de código externo, podemos redefinir el constructor del proxy y usar un WeakSet para almacenar el proxy como lo hace Matthew Brichacek. No uso una clase porque, de lo contrario, Proxy tendrá un prototipo y será detectable que Proxy ha cambiado.
const proxy_set = new WeakSet()
window.Proxy = new Proxy(Proxy,{
construct(target, args) {
const proxy = new target(...args)
proxy_set.add(proxy)
return proxy
}
})
const a = external_script()
console.log(proxy_set.has(a)) //true
Mismo método pero con Símbolo como David Callanan
const is_proxy = Symbol(''is_proxy'')
const old_Proxy = Proxy
const handler = {
has (target, key) {
return (is_proxy === key) || (key in target)
}
}
window.Proxy = new Proxy(Proxy,{
construct(target, args) {
return new old_Proxy(new target(...args), handler)
}
})
const a = external_script()
console.log(is_proxy in a) //true
Creo que el primero es mejor porque solo cambias el constructor mientras que el segundo crea un proxy de un proxy mientras el propósito de la pregunta era evitar esto.
No funciona si el proxy se crea dentro de un iframe porque solo hemos redefinido el proxy para el marco actual.
Parece que no hay una forma estándar, pero para el código privilegiado de Firefox puedes usar
Components.utils.isProxy(object);
Por ejemplo:
Components.utils.isProxy([]); // false
Components.utils.isProxy(new Proxy([], {})); // true