Привязка «this» при передаче функции-члена объекта - PullRequest
2 голосов
/ 19 июля 2011

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

Итак, у меня есть

var mySingleton = {
    theObjects : [];
}

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}());

mySingleton.addObject = function(newObj) {
    this.theObjects.push(newObj);
}

Однако, когда я пытаюсь использовать этот код (после добавления нескольких объектов), я получаю сообщение об ошибке that.theObjects - неопределенная ошибка. Это относится к строке в цикле for.

Ответы [ 3 ]

7 голосов
/ 19 июля 2011

Обновление за 2015 год - Используйте Function.bind(), чтобы указать значение this в функции.Затем вместо that вы можете использовать this.

mySingleton.mouseHandler = function (e) {
    for (var indx = 0; indx < this.theObjects.length; indx++) {
        // do something to this.theObjects[indx];
    }
}.bind(mySingleton);

Это не сработает, если вы хотите, чтобы mouseHandler имел контекст элемента 'moused'.Для этого используйте мой оригинальный ответ ниже.

Если вам требуется поддержка IE8 или (небеса запретить) раньше, вам необходимо использовать polyfill .


Поскольку вы вызываете функцию, которая немедленно создает mouseHandler, она запускается в контексте window, а не mySingleton.Так that относится к window.Вместо немедленного вызова, просто измените его на метод, чтобы он выполнялся в контексте mySingleton:

mySingleton.getMouseHandler = function() {
    var that = this;
    return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();

Конечно, поскольку вы уже используете singleton, вы можете просто ссылаться на него напрямую,В вашем обработчике кликов вместо проверки that.theObjects отметьте mySingleton.theObjects.Или в mouseHandler измените var that = this на var that = mySingleton.

Редактируйте: Или передайте контекст своей анонимной функции при вызове:

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}).call(mySingleton);
1 голос
/ 19 июля 2011

Есть несколько популярных способов сделать это.Во-первых, супер-простое решение - просто обратиться к mySingleton напрямую и обойти путаницу, связанную с this.Вместо that.theObjects просто сделайте mySingleton.theObjects и продолжайте свою жизнь, и все будет хорошо.

Однако, существует общая схема для этого связывания.Вот как underscore.js делает это

Проверьте аннотированный источник, чтобы подчеркнуть , где вы найдете это

 _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };
0 голосов
/ 19 июля 2011

Другие ответы здесь также верны.Предоставление моей точки зрения здесь на случай, если это поможет.

Ключ к пониманию того, почему код не ведет себя так, как вы ожидаете, требует понимания того, как this работает в JavaScript.Проблема в том, что this зависит от того, как вызывается функция.

Во-первых, если вы вызываете функцию в стиле метода, this - это то, что вы ожидаете:

mySingleton.mouseHandler(); // this === mySingleton

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

var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton

Если вы отсоединяете функцию, this становится объектом глобальной области (window)

var foo = mySingleton.mouseHandler;
foo(); // this === window

И, наконец, вы можете заставить this быть чем-то другим, используя call или apply:

var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy

. Вывод заключается в том, что this определяется во время выполнения в зависимости отфункция была вызвана.Часто фреймворки, которые позволяют вам делать «классы», абстрагируют эти детали от вас, неявно применяя шаблон связывания от вашего имени.Вот почему он работал, и больше не работает.

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

Вот статья, которую я написал на эту тему несколько лет назад и которая более детально описана: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...