Почему эти две функции JavaScript не эквивалентны?

Эта функция работает:

function refreshCodeMirror(){
  $("textarea").each(function(){
    var codeMirror = $(this).data('codeMirror');
    setTimeout(function(codeMirror){
      return function () {
        codeMirror.refresh();
      }
    }(codeMirror), 10)

  });
}

Но когда я попытался упростить это до этого:

function refreshCodeMirror(){
  $("textarea").each(function(){
    var codeMirror = $(this).data('codeMirror');
    setTimeout(codeMirror.refresh, 10)

  });
}

Упрощение не работает.

Некоторый (возможно, не относящийся к делу) контекст:

Функция refreshCodeMirror вызывается в onclick для заголовка вкладки начальной загрузки в шаблоне django:

   <div class="row">
        <div class="col-md-12">
            <ul class="nav nav-tabs" role="tablist">
                {% for field in form_tab_fields %}
                    <li role="presentation"{% if forloop.first %} class="active"{% endif %}>
                        <a class="tab-header" href="#{{ field.id_for_label }}_tab" data-toggle="tab" onclick="refreshCodeMirror()">{{ field.label_tag }}</a>
                    </li>
                {% endfor %}
            </ul>
            <div class="tab-content">
                {% for field in form_tab_fields %}
                    <div role="tabpanel"
                         class="form-group tab-pane{% if forloop.first %} active{% endif %}"
                         id="{{ field.id_for_label }}_tab">
                            {{ field }}
                    {{ field.errors }}
                    </div>
                {% endfor %}
            </div>
        </div>
    </div>

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

1 ответ

Решение

Проблема в том, что вы теряете контекст.

Функции, которые не вызываются непосредственно в объекте и не связаны вручную с контекстом, вызываются в глобальном контексте.

var obj = {
  print: function() {
    document.write('<pre>' + this + '</pre>');
  }
};

obj.print();
var p = obj.print;
p();

Когда вы используетеsetTimeoutили другие подобные функции, это как делать это:

function setTimeout(f, time) {
  wait(time);
  f();
}

Так что ваши refresh функция ожидает this быть равным вашему codeMirror экземпляр, но вместо этого он равенwindowили жеundefined(в зависимости от того, находитесь ли вы в строгом режиме).

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

setTimeout(function() {
  codeMirror.refresh();
}, 10);

Другой способ, это использоватьbind передать ему копию функции с this установить на правильный объект.

setTimeout(codeMirror.refresh.bind(codeMirror), 10);
Другие вопросы по тегам