对自定义类的addEventListener.call(ob,..)进行非法调用



我在addEventListener.call(ob)上收到非法调用错误。这是我的示例代码:

function MyClass(name) {
var target = document.createTextNode(null);
this.addEventListener = target.addEventListener.bind(target);
this.removeEventListener = target.removeEventListener.bind(target);
this.dispatchEvent = target.dispatchEvent.bind(target);
this.MyName = name;
this.ModifyName = function(newName) {
this.MyName = newName;

this.dispatchEvent(new Event("Change"));
};
}
MyClass.prototype = EventTarget.prototype;

function changeHandler() {
console.log(`From changeHandler`);
}
function changeHandler2() {
console.log(`From changeHandler2`);
}
function test() {
var ob = new MyClass("OrgName");
ob.addEventListener("Change", changeHandler);
addEventListener.call(ob, "Change", function() { changeHandler() }); // <-- Illegal invocation
console.log(ob.MyName);
ob.ModifyName("UpdatedName");
console.log(ob.MyName);
}
test();

更新

实际上,我已经为XmlHttpRequest创建了代理类,用于拦截所有ajax请求/响应。在angular开始使用addEventListener.call(xhr,"readystatechange",callback(之前,它一直工作良好。这里,xhr是我的代理类的对象。我无法控制角度代码。我只能修改我的代理类。我可以通过上面的示例代码重现"非法调用"错误。所以上面的示例代码就像我的场景的模拟器。

有什么建议吗?

以上注释只是猜测代码失败的原因。

尽管幸运的是,所有其他代码气味都不会导致抛出错误,但test()(代码示例的第30行(中最大的错误是调用addEventListener.call(ob, "Change", function() { changeHandler() });

调用是非法的,因为它将欺骗绑定上下文。它失败是因为globalwindow对象不等于ob。如果调用例如ob.addEventListener.call(ob, "Change", changeHandler2);,一切都很好,因为上下文仍然针对相同的引用。

function MyClass(name) {
var target = document.createTextNode(null);
this.addEventListener = target.addEventListener.bind(target);
this.removeEventListener = target.removeEventListener.bind(target);
this.dispatchEvent = target.dispatchEvent.bind(target);
this.MyName = name;
this.ModifyName = function(newName) {
this.MyName = newName;

this.dispatchEvent(new Event("Change"));
};
}
MyClass.prototype = EventTarget.prototype;

function changeHandler() {
console.log(`From changeHandler`);
}
function changeHandler2() {
console.log(`From changeHandler2`);
}
function test() {
var ob = new MyClass("OrgName");
ob.addEventListener("Change", changeHandler);

// allowed because the context still does target the same reference.
ob.addEventListener.call(ob, "Change", changeHandler2);
// - not allowed because it tries to cheat about the bound context, ...
//   ...it fails because `global` or `window` object does not equal `ob`.
//
// //addEventListener.call(ob, "Change", function() { changeHandler() });
console.log(ob.MyName);
ob.ModifyName("UpdatedName");
console.log(ob.MyName);
}
test();
.as-console-wrapper { min-height: 100%!important; top: 0; }

因为OP的例子无论如何都已经使用了EventTarget(第16行状态为MyClass.prototype = EventTarget.prototype;(,on可以切换到class语法,并以(c(更精简的方式实现MyClass。。。

class MyClass extends EventTarget {
constructor(myName) {
super();
// make it cheat proof, preserve the original `this` context.
var eventTarget = this;
this.name = myName;
this.modifyName = function(newName) {
// using the outer `eventTarget` prevents a `this` context change
// from outside via e.g. `ob.modifyName.call(obj_2, "UpdatedName_2")`
eventTarget.name = newName;
eventTarget.dispatchEvent(new Event("namechange", {
srcElement: eventTarget,
currentTarget: eventTarget,
target: eventTarget
}));
};
}
}

function changeHandler(evt) {
console.log('From changeHandler');
// console.log('From changeHandler :: evt :', evt);
}
function changeHandler_2(evt) {
console.log('From changeHandler_2');
// console.log('From changeHandler_2 :: evt :', evt);
}
function test() {
var ob = new MyClass("OrgName");
ob.addEventListener("namechange", changeHandler);
var obj_2 = { name: "OrgName_2" };
obj_2.dispatchEvent = ob.dispatchEvent.bind(obj_2);
// allowed because the context still does target the same reference.
ob.addEventListener.call(ob, "namechange", changeHandler_2);
// // throws invocation error cause it tries to cheat about the context.
// ob.addEventListener.call(obj_2, "namechange", changeHandler_2);
console.log(ob.name);
console.log(obj_2.name);
ob.modifyName("UpdatedName");
ob.modifyName.call(obj_2, "UpdatedName_2");
console.log(ob.name);
console.log(obj_2.name);
}
test();
.as-console-wrapper { min-height: 100%!important; top: 0; }

最新更新