Javascript, странное поведение в замыкании - PullRequest
3 голосов
/ 19 февраля 2010

Я играл (читай: учился) с Javascript и наткнулся на что-то, насколько я понимаю, кажется очень странным. Это связано с замыканиями и ссылкой, которая, кажется, теряет свою важность для браузера.

Используемый мной браузер: Chromium 5.0.307.7 .

В любом случае, вот код:

HTMLElement.prototype.writeInSteps = function() {
  var i = 0;
  var elem = this;
  var args = arguments;

  function step() {
    elem.innerHTML += args[i];

    if(i < args.length) {
      i += 1;
    } else {
      elem.innerHTML = "";
      i = 0;
    }


    setTimeout(step, 500);
  }

  step();
}

Здесь происходит то, что первый аргумент записывается в правильный HTMLElement, а все последующие - нет. Похоже, что после первого аргумента следующие аргументы записываются в какой-то другой элемент, на который теперь ссылается 'elem'.

Я должен также упомянуть, что, кажется, это происходит только тогда, когда я пишу что-то непосредственно после вызова этой функции, например:

div.writeInSteps("This", " is", " not", " working");
$id("body").innerHTML += "Doh!";

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

Если я вместо этого изменю код выше на:

HTMLElement.prototype.writeInSteps = function() {
  var i = 0;
  var e = this.id;
  var args = arguments;

  function step() {
    var elem = $id(e);
    elem.innerHTML += args[i];

    if(i < args.length) {
      i += 1;
    } else {
      elem.innerHTML = "";
      i = 0;
    }


    setTimeout(step, 500);
  }

  step();
}

Все денди. Мой вопрос: что на самом деле происходит за кулисами в первой версии?

РЕДАКТИРОВАТЬ : Обновлено с запрошенными подробностями о "... написать что-то сразу после ..." и использовании браузера в соответствии с запросом ntownsend Брайан Мэтьюз , я не уверен, как предоставить тестовую страницу, не делая этот вопрос чрезмерно загроможденным.

Ответы [ 3 ]

3 голосов
/ 19 февраля 2010

Я подозреваю, что это проблема DOM, а не проблема JavaScript.

Я предполагаю, что что-то мутирует в предка элемента, к которому вы пытаетесь писать поэтапно. Например, если установлено значение innerHTML родительского элемента (я думаю, что даже к той же самой строке), ссылка на элемент, которая у вас есть, будет на элемент, которого больше нет в DOM. Повторное получение элемента по идентификатору каждый раз поможет обойти эту проблему.

0 голосов
/ 19 февраля 2010

Если вы заменяете innerHTML предка elem (элемент body, как в вашем примере), то elem больше не существует. Когда step после того, как исходное elem уничтожено, то, на что ссылается elem, будет чем-то другим.

Правильно, что браузер должен сделать, вероятно, должен удалить ссылку elem, но это не похоже, что он делает это.

0 голосов
/ 19 февраля 2010

я предполагаю, что выполнение обратного вызова setTimeout(step, 500); имеет очень мало представления о том, кто такой this - вы вызываете step текущего элемента или, может быть, HTMLElement.prototype.writeInSteps.step()?

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

Конечно, Джефф тоже может быть прав. innerHTML только для чтения по спецификации, и запись в него может или не может перестроить все дерево (и уничтожить все ссылки).

...