Странное поведение атрибута при использовании HTMLElement в качестве прототипа пользовательской функции - PullRequest
0 голосов
/ 08 февраля 2012

Я пытаюсь «расширить» (не уверен, что является правильным термином для такого рода расширений) элемент DOME до переопределить определенные свойства (базовый элемент). Для этого я динамически создаю функцию, прототип которой является элементом DOME, который я хочу расширить, а затем создаю экземпляр объекта с этой функцией (расширенный элемент).

При назначении значения расширенному элементу ожидаемый результат будет таким: свойство с заданным именем будет создано в расширенном элементе , оставляя базовый элемент без изменений, даже если у него есть свойство с этим то же имя. Выполнение этого с простым объектом в качестве базового элемента работает безупречно, но когда использует 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, а описание интерфейса реализации браузера. То, что это так, влияет на то, как прототип «наследования» в этом случае работает, делая реализацию собственного метода установки для действия значения вместо создания свойства расширенного объекта. (Это может поддерживаться исключением, генерируемым в IE, утверждающим, что я пытаюсь получить доступ к нативной функции / свойству из неродного элемента).

1 Ответ

1 голос
/ 08 февраля 2012

Кажется, это сработало:

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: он всегда делегировал установщику прототипа, то есть 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 очень четко указывает для среды выполнения, что мы хотим создать новую, тем не менее, так и получается.

...