Passing Proxy object as thisArgument to apply throws TypeError: Illegal Invocation
Passing Proxy object as thisArgument to apply throws TypeError: Illegal Invocation
我正在尝试捕获对 Storage
的呼叫。据我所知,有两种方法可以调用 setItem
或 getItem
:
sessionStorage.setItem("foo", "bar");
let item = sessionStorage.getItem("foo");
Storage.prototype.setItem.call(sessionStorage, "foo", "bar");
let item2 = Storage.prototype.getItem.call(sessionStorage, "foo");
在代码段中使用 sessionStorage
会引发安全错误,因此 here's the code in JS Fiddle
TLDR:如果我这样做,我可以处理所有情况,但它看起来很老套。有没有 better/cleaner 方法来实现我的目标? (注意:我无法控制来电者,这就是我涵盖这两种情况的原因):
try {
console.log("\n\n");
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
if (p === "__this") {
// kinda hacky, but allows us to unwrap Proxy for binding
return getTarget;
}
console.log("sessionStorage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
// unpack proxy if we're double-wrapped (both target and thisArg are Proxy)
return Reflect.apply(applyTarget, thisArg.__this, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(ss, "foo", "3");
console.log(`Storage.prototype.getItem: ${gi.call(ss, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 3
Storage.prototype Worked
除了包含一个“隐藏的”__this
属性 以便调用者可以“解开”Proxy
对象并获取对原文sessionStorage
。有更好的方法吗?
背景
如果有帮助,这里是仅包装 sessionStorage
或 Storage
而非两者的示例:
换行sessionStorage
对于 apply
陷阱,我必须传递 getTarget
而不是 thisArg
因为后者是 Proxy
对象,如果我传递它一个 Illegal Invocation
抛出错误。
try {
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
console.log("sessionStorage.get called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
ss.setItem("foo", "1");
console.log(`Proxy.sessionStorage.getItem: ${ss.getItem("foo")}`);
console.log(`Proxy.sessionStorage Worked`)
} catch (e) {
console.log(`Proxy.sessionStorage Caught ${e.stack}`);
}
结果:
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
Proxy.sessionStorage.getItem: undefined
Proxy.sessionStorage Worked
换行Storage.prototype
这里我必须首先创建一个带有单独 prototype
的新对象,因为 prototype
属性 描述符 Storage
的 configurable
设置为 false
。完成后,我基本上必须通过传递“展开的”getTarget
而不是 thisArg
.[=45 指向的 Proxy
实例来做与前一个案例相同的事情=]
try {
console.log("\n\n");
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
console.log("apply failed when passing thisArg");
return Reflect.apply(applyTarget, getTarget, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(sessionStorage, "foo", "2");
console.log(`Storage.prototype.getItem: ${gi.call(sessionStorage, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 2
Storage.prototype Worked
我认为你把这件事想得太复杂了。没有理由在这里涉及代理,只是猴子修补这两个方法:
const proto = Storage.prototype;
const originalSet = proto.setItem;
const originalGet = proto.getItem;
Object.assign(proto, {
setItem(key, value) {
console.log(`Setting ${JSON.stringify(key)} to ${JSON.stringify(value)} on a ${this.constructor.name}`);
return originalSet.call(this, key, value);
},
getItem(key) {
console.log(`Getting ${JSON.stringify(key)} from a ${this.constructor.name}`);
return originalGet.call(this, key);
},
});
我正在尝试捕获对 Storage
的呼叫。据我所知,有两种方法可以调用 setItem
或 getItem
:
sessionStorage.setItem("foo", "bar");
let item = sessionStorage.getItem("foo");
Storage.prototype.setItem.call(sessionStorage, "foo", "bar");
let item2 = Storage.prototype.getItem.call(sessionStorage, "foo");
在代码段中使用 sessionStorage
会引发安全错误,因此 here's the code in JS Fiddle
TLDR:如果我这样做,我可以处理所有情况,但它看起来很老套。有没有 better/cleaner 方法来实现我的目标? (注意:我无法控制来电者,这就是我涵盖这两种情况的原因):
try {
console.log("\n\n");
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
if (p === "__this") {
// kinda hacky, but allows us to unwrap Proxy for binding
return getTarget;
}
console.log("sessionStorage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
// unpack proxy if we're double-wrapped (both target and thisArg are Proxy)
return Reflect.apply(applyTarget, thisArg.__this, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(ss, "foo", "3");
console.log(`Storage.prototype.getItem: ${gi.call(ss, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 3
Storage.prototype Worked
除了包含一个“隐藏的”__this
属性 以便调用者可以“解开”Proxy
对象并获取对原文sessionStorage
。有更好的方法吗?
背景
如果有帮助,这里是仅包装 sessionStorage
或 Storage
而非两者的示例:
换行sessionStorage
对于 apply
陷阱,我必须传递 getTarget
而不是 thisArg
因为后者是 Proxy
对象,如果我传递它一个 Illegal Invocation
抛出错误。
try {
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
console.log("sessionStorage.get called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
ss.setItem("foo", "1");
console.log(`Proxy.sessionStorage.getItem: ${ss.getItem("foo")}`);
console.log(`Proxy.sessionStorage Worked`)
} catch (e) {
console.log(`Proxy.sessionStorage Caught ${e.stack}`);
}
结果:
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
Proxy.sessionStorage.getItem: undefined
Proxy.sessionStorage Worked
换行Storage.prototype
这里我必须首先创建一个带有单独 prototype
的新对象,因为 prototype
属性 描述符 Storage
的 configurable
设置为 false
。完成后,我基本上必须通过传递“展开的”getTarget
而不是 thisArg
.[=45 指向的 Proxy
实例来做与前一个案例相同的事情=]
try {
console.log("\n\n");
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
console.log("apply failed when passing thisArg");
return Reflect.apply(applyTarget, getTarget, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(sessionStorage, "foo", "2");
console.log(`Storage.prototype.getItem: ${gi.call(sessionStorage, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 2
Storage.prototype Worked
我认为你把这件事想得太复杂了。没有理由在这里涉及代理,只是猴子修补这两个方法:
const proto = Storage.prototype;
const originalSet = proto.setItem;
const originalGet = proto.getItem;
Object.assign(proto, {
setItem(key, value) {
console.log(`Setting ${JSON.stringify(key)} to ${JSON.stringify(value)} on a ${this.constructor.name}`);
return originalSet.call(this, key, value);
},
getItem(key) {
console.log(`Getting ${JSON.stringify(key)} from a ${this.constructor.name}`);
return originalGet.call(this, key);
},
});