1
<div id="menu">
<button data-action="save">Сохранить</button>
<button data-action="load">Загрузить</button>
<button data-action="search">Поиск</button>
</div>

<script>
function Menu(elem) {
this.save = function() {
  alert( 'сохраняю' );
};
this.load = function() {
  alert( 'загружаю' );
};
this.search = function() {
  alert( 'ищу' );
};

var self = this;     // зачем тут this, объясните пожалуйста дав полноценный ответ

elem.onclick = function(e) {
  var target = e.target;
  var action = target.getAttribute('data-action');
  if (action) {
    self[action]();  // как работает это выражение?
  }
};
}

new Menu(menu);

  • 1
    @КонстантинБашаркевич - ему непонятно - 1. что такое this при вызове функции как конструктора, 2. что такое замыкание (closure), где переменная self доступна в обработчике onclick, a this в этом обработчике, отличается от this в Menu. –  Aug 05 '16 at 16:04
  • @Igor да, я на комментарии не сразу обратил внимание, прошу прощения – qodunpob Aug 05 '16 at 16:05
  • а остальной код, кроме этих двух строчек понятен? – Grundy Aug 05 '16 at 16:50
  • Да, с остальным кодом я разобрался. – Никита Aug 05 '16 at 20:57

3 Answers3

6

В данном случае функция Menu используется в качестве конструктора. При ее вызове с оператором new: new Menu(menu), внутри нее this указывает на новый создаваемый объект.

Внутри обработчика клика, this уже будет указывать на элемент, по которому кликнули и чтобы вызывать внутри обработчика методы создаваемого объекта Menu, ссылка на этот объект сохраняется в переменную self, которая и используется внутри обработчика.

Подробнее про потерю контекста можно узнать в вопросе: Потеря контекста вызова


Для обращения к полям объекта в яваскрипте предусмотрено две нотации

  1. Dot-notation - обращение к полю через точку

    var o = {
      prop: 1;
    };
    o.prop;// 1
    

    Данная нотация широко распространена, но имеет ограничения на имена полей, к которым может быть применена. Так, для использования этой нотации, имя поля должно быть валидным идентификатором.

  2. Bracket-notation - обращение к полю с использованием скобок []

    var o = {
      prop: 1;
    };
    o["prop"];// 1
    

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

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


Без промежуточной переменной можно обойтись, если использовать стрелочные функции

function Menu(elem) {
  this.save = function() {
    console.log('сохраняю');
  };
  this.load = function() {
    console.log('загружаю');
  };
  this.search = function() {
    console.log('ищу');
  };

elem.onclick = (e) => { var target = e.target; var action = target.getAttribute('data-action'); if (action) { thisaction; } }; }

new Menu(menu);

<div id="menu">
  <button data-action="save">Сохранить</button>
  <button data-action="load">Загрузить</button>
  <button data-action="search">Поиск</button>
</div>
Grundy
  • 81,538
  • Круто. Но все же думаю, не стоит поощрять авторов подобными ответами:) – dirkgntly Aug 05 '16 at 18:28
  • @dDevil, вообще, если б можно было сразу отметить как дубликат двух вопросов - я бы отметил :-) – Grundy Aug 05 '16 at 18:29
  • Удивлён что никто не отметил - неявное создание ссылок на элементы с id браузером использовать никак не стоит. –  Aug 05 '16 at 18:47
  • @Other, мне кажется в данном случае это не имеет значения – Grundy Aug 05 '16 at 18:49
  • Всё же - если кто-то просит объяснения, нужно дать ему как можно больше знаний, а эти переменные - довольно коварный хак, о котором стоит знать. Хотя это Ваш ответ - пишите что хотите :) –  Aug 05 '16 at 18:52
  • @Other, уже написал, и так не мало получилось – Grundy Aug 05 '16 at 18:53
  • И всё же кажется с водой перебор. –  Aug 05 '16 at 18:54
2
function Menu(elem) {
    this.save = function() {
        alert( 'сохраняю' );
    };

    this.load = function() {
        alert( 'загружаю' );
    };

   this.search = function() {
       alert( 'ищу' );
   };

   var self = this;  // тут self будет равен объекту данной функции (класса) Menu

   elem.onclick = function(e) {
       var target = e.target;
       var action = target.getAttribute('data-action'); // данные атрибута в котором записано имя метода
       if ( action ) {
           self[action]();  // вызываем метод нашего объекта Menu
       }
   };
}

new Menu(menu);

Описал то что спрашивали ))

webkostya
  • 1,665
  • Почему self будет равен именно объекту, как он на него указывает? – Никита Aug 05 '16 at 20:51
  • Функция Menu вызывается как объект new Menu. Ну а в self мы записываем this что является ссылкой на родительский объект. – webkostya Aug 05 '16 at 21:42
  • То есть по сути если бы функция Main не вызывалась как объект, то переменная self указывала на window? – Никита Aug 05 '16 at 21:48
  • Вообще тут self указывает на объект не только потому что функция у нас вызывается как объект, но еще и потому что ее тут перезаписали, если бы записали вот так _self то получилось бы что ссылка на объект записалась бы в простую переменную, и self сохранил бы свое первоначальное значение. Надеюсь я никого не запутал )) – webkostya Aug 05 '16 at 22:00
1

Внутри второй функции будет свой this, не имеющий ничего общего с this из "верхнего" кода. Для доступу к этому "верхнему" this мы и объявили переменную. Не найдя такой переменной js внутри функции, js полезет наверх, и упрётся в эту "глобальную"(в данном контексте) переменную self

  • про выражение неверно, в коде даже массива нет – Grundy Aug 05 '16 at 16:41
  • @Grundy, просветите меня тогда пожалуйста:). Могу предположить, что это динамический вызов одной из функции save, load, search. Если так, дайте пожалуйста статью поподробнее про это. Если нет - все равно расскажите:) – Vyacheslav Potseluyko Aug 05 '16 at 17:29
  • 1
    @VyacheslavPotseluyko, минутка педантичности)) В JavaScript нет ассоциативных массивов, в нем есть "объекты, которые ведут себя как ассоциативные массивы" или другими словами "хеш таблицы". И это не выражение, а обращение к свойствам объектам или доступ к элементам хеш таблицы по ключу. Поэтому правильней говорить не массив, а объект. И не выражение, а доступ к свойству объекта. Как-то так: http://www.quirksmode.org/js/associative.html – Alex Krass Aug 05 '16 at 18:04
  • @AlexKrass почаще бы такие минутки). Спасибо) – Vyacheslav Potseluyko Aug 05 '16 at 20:01
  • Я все же не до конца понимаю. Почему переменная self указывает именно на объект, а не на window? Прошу прощение если сильно примитивный вопрос, я просто еще не опытный. – Никита Aug 05 '16 at 21:42
  • @Игорь, ключевое слово this указывает на контекст исполнения. Внутри функции на саму функцию Main, а при использовании обработчиков на объект window. В данном случае пока находимся в функции сохраняем контекст Main в промежуточную переменную и используем уже ее не теряя контекст. Поэтому внутри self все так же ссылается на Main т.к. мы ее сохранили выше, а вот сама this ссылается на window. Подробнее читайте про области видимости в JS и замыканиях. – Alex Krass Aug 05 '16 at 22:32