Прототипы - присваивание свойства

313
15 августа 2017, 17:00

Есть задача: при отсутствии textContent'а - перенять принцип работы innerText'а.

Вот правильное решение:

  if (document.documentElement.textContent === undefined) {

    Object.defineProperty(HTMLElement.prototype, "textContent", {
      get: function() {
        return this.innerText;
      },
      set: function(value) {
        this.innerText = value;
      }
    });
  }

Но меня не столько интересует верный ответ, сколько то, почему вот этот код:

if (HTMLElement.prototype.textContent === undefined) {
   HTMLElement.prototype.textContent = HTMLElement.prototype.innerText;
}

... не сработает.

Меня интересует:

  1. Что находится внутри innerText'а? Точно такие же геттеры-сеттеры, как и в правильном решении?
  2. Если в innerText'е находится такой же объект с геттер-сеттером, то почему операция присваивания, не делает из .textContent'а - обычную ссылку на тот объект?
  3. Или проблема в контексте this'ов?

Кто знает - объясните всю подноготную происходящего.

Спасибо.

Answer 1

Странно, почему решили стандартное свойство textContent реализовывать через не стандартный стандартный с 2016 г. innerText.

innerText это свойство, в данном случае оно представлено в виде двух функции: getter и setter.

При обращении к свойству - происходит вычисление нужной функции, поэтому нельзя позаимствовать свойство целиком, как обычную функцию.

В этом случае

HTMLElement.prototype.innerText;

Происходит попытка получить значение innerText, и так как HTMLElement.prototype не является элементом, кидается исключение.

В правильном решении, добавляется новое свойство, внутри, которого вызывает уже имеющееся свойство, так как в момент вызова this уже существующий элемент все работает.

Альтернативным решением может быть копирование определения свойства, для этого можно получить property descriptor, с помощью Object.getOwnPropertyDescriptor

И затем добавить его же, но уже с нужным именем:

var innerTextPropertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype,'innerText');
Object.defineProperty(HTMLElement.prototype, "textContent", innerTextPropertyDescriptor);
Answer 2

Что находится внутри innerText'а? Точно такие же геттеры-сеттеры

Да.

почему операция присваивания, не делает из .textContent'а - обычную ссылку на тот объект?

Потому что при присваивании для левой части вызывается сеттер (или создаётся свойство), а для правой - геттер. Соответственно, ты присваиваешь значение, возвращённое геттером, а не сам геттер.

Или проблема в контексте this'ов?

Нет.

Вот правильное решение

Нет.

textContent - IE9+ и все остальные
innerText - IE6+ и все кроме FF45-
defineProperty - IE9+ (IE8 с оговорками) и все остальные

Получается, что даже в идеальном случае это решение покрывает единстыенный браузер IE8 (и то, надо это проверять, причём, желательно, в самом браузере, а не в эмуляции).

И это не говоря о том, что эти свойства не являются взаимозаменяемыми, из-за чего в jQuery не стали так делать. Вот, запусти это в хроме:

var div = document.querySelector("div") 
 
console.log(JSON.stringify(div.textContent)) 
console.log(JSON.stringify(div.innerText))
<div> 
  <span>123</span> 
  <script>var any = {}</script> 
</div>

Answer 3

Да, свойство innerText имеет геттер (можно в этом убедится в консоли браузера), поэтому для вычисления результата, который нужно присвоить свойству textContent, он будет вызван. Поскольку геттер вызывается при этом не как метод объекта, а просто функция, то значением this внутри геттера будет undefined. В реализации геттера по всей видимости (если я правильно понимаю, это зашито в движке браузера) присутствует проверка на этот случай и результатом будет ошибка TypeError: 'get innerText' called on an object that does not implement interface HTMLElement.

READ ALSO
Как установить value по умолчанию в redux form?

Как установить value по умолчанию в redux form?

Недавно начала изучение redux и столкнулся с проблемой, не знаю как поставить дефолтное значениеВот пример кода:

429
Как удалить заданные символы в строке

Как удалить заданные символы в строке

Всем добра! Есть строка такого вида: return showWiki({w: 'wall290708618_2192'}, false, event);

309