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

Strange attribute behaviour when using HTMLElement as prototype of custom Function

本文关键字:属性 性行为 原型 HTMLElement 自定义函数 使用      更新时间:2023-09-26

我正在尝试"扩展"(不确定这类扩展的正确术语是什么)一个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,而是一个浏览器接口的实现。在这种情况下,这样会影响原型"继承"的工作方式,使值的本地setter实现起作用,而不是在扩展对象上创建属性。(IE中抛出的异常可以支持这一点,该异常表示我正试图从非本机元素访问本机函数/属性)。

这似乎已经完成了任务:

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向运行时明确表示,我们想要创建一个新的,所以这就成功了。