в JavaScript путаница с «этим» - как вы делаете это _this_, когда метод вызывается событием? - PullRequest
2 голосов
/ 20 апреля 2011

с примером "class":

var Klass = {
 'name': 24,
 'init': function() { 
            var input1 = document.getElementsByTagName('input')[0];
            input1.addEventTrigger('change',func1,true);
          },
 'func1': function( arg ) {
            // func called by an event
            if( arg instanceof Event ) {
               console.log( this.name );
               arg.preventDefault();
            }
            // func called "normally"
            else console.log( this.name );
           }
}

, насколько я понимаю:

  • , если Klass.func1 вызывается в обычном контексте сценария, будет записано 24в консоль, поскольку Klass - это this
  • , если <closure>.func1 вызывается триггером onchange элемента ввода, имя входа будет записано в консоль, поскольку элемент триггер это

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

this приводит к двум вопросам:

  1. правильный ли выложенный сценарий?
  2. , если func1 вызывается Event, как мне переместить замыкание (или область (?)) так, чтобы это было оригинальным Klass вместо Event.target?

Ответы [ 3 ]

3 голосов
/ 20 апреля 2011
  1. Если относительно вашего понимания, тогда да.

  2. var Klass = { '$this': this, 'name': 24, ... Тогда используйте $ this внутри func1

Вы также можете определить Klass как экземпляр внутри замыкания, а не использовать нотацию объекта, чтобы ваш $ this экземпляр был закрытым при использовании var $ this = this;

EDIT: $ this в этом случае будетэкземпляр DOMWindow

2 голосов
/ 20 апреля 2011

Понятие «это» является одним из наиболее неправильно понятых аспектов JavaScript. Было много вопросов о StackOverflow, в которых обсуждается понятие «это», а также многочисленные посты в блогах, статьи и т. Д. Я не буду претендовать на звание эксперта, но я верю, что могу очистить некоторые мутные воды, очевидные в ваш вопрос.

Значение this зависит от контекста, в котором выполняется текущая функция. Рассмотрим следующий код:

this; // Alone, will be 'window' or DOMWindow

Теперь в контексте простого объекта:

var Klass = { 
   self: this // -> DOMWindow
}

Если бы мы исследовали Klass.self, мы обнаружили бы, что это все еще DOMWindow, а не фактически Klass. Это связано с тем, что значение this было оценено во время создания Klass и было создано на верхнем уровне в контексте DOMWindow.

Другой пример:

var Klass = { 
   func: function(){
       console.log(this);
   }
}
Klass.func(); // -> Klass

Теперь, если мы посмотрим на значение 'this' в результате выполнения Klass.func (), это фактически будет Klass. Это потому, что func был выполнен в рамках Klass. Другой пример:

var Klass = { 
   func: function(){
       console.log(this);
   }
}

var anotherFunc = Klass.func;
anotherFunc(); // -> this is now DOMWindow

anotherFunc - это ссылка на Klass.func, но не в контексте Klass. Мы просто использовали Klass, чтобы получить дескриптор func, но как только мы получили этот дескриптор, anotherFunc больше не имеет понятия о Klass. Это то же самое, что происходит с обработкой событий - часто вы передаете ее дескриптор функции, которую вы хотите выполнить. Как вы поняли, что ручка не имеет значения.

var Klass = { 
   func: function(){
       console.log(this);
   }
}

var anotherFunc = Klass.func;
anotherFunc.call(Klass); //-> this is now Klass again!

В приведенном выше примере мы выполняем anotherFunc с помощью функции 'call'. Это способ форсировать контекст выполнения. Если мы хотим передать аргументы функции, вы все равно можете сделать это:

anotherFunc.call(Klass, someArgument);

Есть много способов справиться с контекстом и «этим» в JavaScript. Приведенные выше примеры в основном касаются основных «объектов». Вы получаете еще несколько вариантов того, как обращаться с вещами, если ваш объект сам по себе является функцией. Но мы оставим это на время! Что касается вашего примера, вы можете получить желаемое поведение следующим образом:

var Klass = {
    'name': 24,
    'init': function() { 
        var input1 = document.getElementsByTagName('input')[0];
        input1.addEventTrigger('change',Klass.func1,true); // note the addition of Klass to the func1 reference here.  Because just 'func1' is undefined in the scope otherwise.
    },
    'func1': function( arg ) {
        console.log(Klass.name );

        if( arg instanceof Event ) {
            arg.preventDefault();
        }
    }
}

РЕДАКТИРОВАТЬ: В ответ на ваш комментарий:

 addEventTrigger('change',Klass.change.call(Klass),true) 

Это вызовет Klass.change сразу, а не во время процесса обратного вызова. Вместо этого обратному вызову будет присвоен результат Klass.change, который не является вашим желаемым поведением. Вместо этого оберните его в анонимную функцию:

 addEventTrigger('change', function(){
     Klass.change();
 }, true);

Обратите внимание, что мне не нужно использовать call () там, потому что я на самом деле выполняю его из Klass, предоставляя ему контекст выполнения Klass. Вы можете вызывать Klass.change () из любого места, и значение 'this' внутри change () будет Klass. Разница между непосредственным вызовом и использованием его для передачи дескриптора функции обратному вызову невелика, но просто помните, что «this» - это все, когда и как функция на самом деле получает выполненную , не назначенную или не переданную вокруг.

Google javascript на этом сайте: stackoverflow.com , где вы найдете множество других замечательных вопросов по этой теме, многие из которых включают отличные внешние ссылки.

1 голос
/ 20 апреля 2011

Либо измените свой обратный вызов на function() { Klass.func1(event) }

Или использование оболочки будет сохранять контекст (т.е. this) функции при вызове в качестве обратного вызова.

function wrapper(callback, context) {
    return function() {return callback.apply(context, arguments);}
}

Пример:

// Simple function
echo = function bob(msg){return this.greeting + " " + msg;}

// Wrap two functions
var c1 = wrapper(echo, {greeting: "Hello"});
var c2 = wrapper(echo, {greeting: "Goodbye"});

console.log(c1("Bob"));
// Hello Bob
console.log(c2("Sam"));
// Goodbye Bob
...