使用HTMLElement作为自定义函数原型时的奇怪属性行为

时间:2012-02-08 18:30:34

标签: javascript dom function-prototypes

我正在尝试“扩展”(不确定这种扩展的正确用语是什么)覆盖某些属性的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)

应用:

我打算做的是创建一个带有覆盖“value”属性的输入元素的扩展实例。这将允许将此对象传递给验证器插件,该插件将使用重写的值来运行其所有验证规则,并且在任何错误的情况下原始对象将保持完整,并且扩展的对象将由验证器完成所有修改。

由于IE中可能无法解决的异常,我们不打算进一步应用这种方法,但我仍然对这种奇怪的行为感兴趣。

问题可能是什么:

我认为这个行为来自事实,而不是DOMElement属性和函数不是纯粹的Javascript,而是浏览器实现的Interface的说明。这样会影响原型“继承”在这种情况下的工作方式,使得值的本机setter实现代替在扩展对象上创建属性。 (这可以通过IE中抛出的异常来支持,声明我正在尝试从非本机元素访问本机函数/属性。)

1 个答案:

答案 0 :(得分:1)

这似乎已经成功了:

var base = $("<input type='text' />")[0];
base.value = "original";

function MyClass() {
  Object.defineProperty(this, "value", {
    value: null,
    configurable: true,
    enumerable: true,
    writable: true
  });
}
MyClass.prototype = base;

var obj = new MyClass();
obj.value = "modified";

console.log(obj.value);    //"modified" (OK)
console.log(base.value);   //"original" (OK)

问题是您的obj实例从未拥有自己的value:它总是委托给原型的setter,即HTMLInputElement.prototype.value。因此obj的所有实例都会共享同一个value属性。

换句话说,说obj.value = "modified"与说Object.getPrototypeOf(obj).value = "modified",当然还有Object.getPrototypeOf(obj) === base相同。

我的第一次尝试是给MyClass自己的每个实例value属性,如下所示:

function MyClass() {
    this.value = null;
}

但实际上这并不是我们想要的,因为this.value只引用了原型的value:我们没有创建新的属性;我们正在设置现有的。 Object.defineProperty使我们想要创建一个新的运行时非常明确,所以这就是诀窍。