使用 HTMLElement 作为自定义函数原型时出现奇怪的属性行为
我正在尝试“扩展”(不确定此类扩展的正确术语是什么)DOMElement 来覆盖某些属性(基本元素)。为此,我动态创建一个函数,其原型是我要扩展的 DOMElement,然后使用该函数(扩展元素)实例化一个对象。
为扩展元素赋值时,预期结果是在扩展元素中创建具有给定名称的属性,而基本元素保持不变,即使它具有同名的属性。使用普通对象作为基本元素可以完美地完成此操作,但是当使用 DOMElement 作为基本元素时,如果存在同名属性,则基本元素会被修改(在 Chrome 和 Firefox 上。即使在尝试获取属性值时,IE9 也会抛出“无效调用对象”。
测试代码:
var base = $("<input type='text' />")[0];
//Plain object example: var base = {value:null}
base.value = "original";
var MyClass = function(){ };
MyClass.prototype = base;
var obj = new MyClass();
obj.value = "modified";
obj.value //"modified" (OK)
base.value //"modified" (WRONG!) - "original" if plain object is used (OK)
应用程序:
我想要做的是创建具有覆盖“值”属性的输入元素的扩展实例。这将允许将此对象传递给验证器插件,该验证器插件将使用覆盖的值来运行其所有验证规则,并且如果出现任何错误,原始对象将保持不变,而扩展对象将由验证器完成所有修改。
由于 IE 中可能抛出无法解决的异常,我们不打算进一步应用此方法,但我仍然对这种奇怪的行为感兴趣。
问题可能是什么:
我认为这种行为来自这样一个事实:DOMElement 属性和函数不是纯 Javascript,而是浏览器实现的接口的说明。在这种情况下,这种情况会影响原型“继承”的工作方式,从而使值的本机 setter 实现发挥作用,而不是在扩展对象上创建属性。 (这可以通过 IE 中抛出的异常来支持,表明我正在尝试从非本机元素访问本机函数/属性)。
I'm trying to "extend" (not sure what is the correct term for this kind of extensions) a DOMElement to override certain properties (base element). To do so I dynamically create a function whose prototype is the DOMElement I want to extend, and then instance an object with that function (extended element).
When assigning a value to the extended element the expected outcome would be that a property with the given name is created in the extended element, leaving the base element untouched, even if it has a property with that same name. Doing so with a plain object as a base element works flawlessly, but when using a DOMElement as the base element, the base element is modified if a property by the same name exists (On Chrome and Firefox. On IE9 an "Invalid calling object" is thrown even when trying to get the property's value).
Test code:
var base = $("<input type='text' />")[0];
//Plain object example: var base = {value:null}
base.value = "original";
var MyClass = function(){ };
MyClass.prototype = base;
var obj = new MyClass();
obj.value = "modified";
obj.value //"modified" (OK)
base.value //"modified" (WRONG!) - "original" if plain object is used (OK)
Application:
What I intended to do was to create an extended instance of an input element with an override "value" attribute. This would allow passing this object to a validator plugin that would use the overridden value to run all of it's validation rules, and in case of any error the original object would remain intact, and the extended one with all the modifications done by the validator.
Due to the probably unsolvable exception thrown in IE we are not going to further apply this method, but I'm still intrigued by this strange behavior.
What the problem might be:
I think that this behavior comes from the fact than a DOMElement attributes and functions are not pure Javascript, but an exposition of an Interface of the browser implementation. Being so affects how the prototype "inheritance" in this case works, making the native setter implementation for the value to act instead of the creation of the property on the extended object. (This can be supported by the exception thrown in IE, stating that I'm trying to access a native function/property from a non-native element).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这似乎已经成功了:
问题是你的 obj 实例从来没有自己的值:它总是委托给原型的 setter,即 HTMLInputElement .prototype.value。因此
obj
的所有实例都将共享相同的value
属性。换句话说,说
obj.value = "modified"
与说Object.getPrototypeOf(obj).value = "modified"
是一样的,当然还有Object.getPrototypeOf(obj) === 基
。我的第一次尝试是为
MyClass
赋予它自己的每个实例value
属性,如下所示:但这实际上并没有达到我们想要的效果,因为
this.value
只是引用原型的值
:我们没有创建新属性;我们正在设置现有的。不过,Object.defineProperty
使运行时非常明确地知道我们想要创建一个新属性,这样就可以达到目的。This seems to have done the trick:
The problem is that your
obj
instance never had its ownvalue
: it was always delegating to the prototype's setter, i.e. toHTMLInputElement.prototype.value
. So all instances ofobj
would share this samevalue
property.In other words, saying
obj.value = "modified"
was the same as sayingObject.getPrototypeOf(obj).value = "modified"
, and of courseObject.getPrototypeOf(obj) === base
.My first try was to give
MyClass
its own per-instancevalue
property, like so:But this doesn't actually do what we wanted, because
this.value
just references the prototype'svalue
: we're not creating a new property; we're setting the existing one.Object.defineProperty
makes it very explicit to the runtime that we want to create a new one, though, so that does the trick.