<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);
- 31
3 Answers
В данном случае функция Menu используется в качестве конструктора. При ее вызове с оператором new: new Menu(menu), внутри нее this указывает на новый создаваемый объект.
Внутри обработчика клика, this уже будет указывать на элемент, по которому кликнули и чтобы вызывать внутри обработчика методы создаваемого объекта Menu, ссылка на этот объект сохраняется в переменную self, которая и используется внутри обработчика.
Подробнее про потерю контекста можно узнать в вопросе: Потеря контекста вызова
Для обращения к полям объекта в яваскрипте предусмотрено две нотации
Dot-notation - обращение к полю через точку
var o = { prop: 1; }; o.prop;// 1Данная нотация широко распространена, но имеет ограничения на имена полей, к которым может быть применена. Так, для использования этой нотации, имя поля должно быть валидным идентификатором.
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>
- 81,538
-
Круто. Но все же думаю, не стоит поощрять авторов подобными ответами:) – dirkgntly Aug 05 '16 at 18:28
-
@dDevil, вообще, если б можно было сразу отметить как дубликат двух вопросов - я бы отметил :-) – Grundy Aug 05 '16 at 18:29
-
Удивлён что никто не отметил - неявное создание ссылок на элементы с
idбраузером использовать никак не стоит. – Aug 05 '16 at 18:47 -
-
Всё же - если кто-то просит объяснения, нужно дать ему как можно больше знаний, а эти переменные - довольно коварный хак, о котором стоит знать. Хотя это Ваш ответ - пишите что хотите :) – Aug 05 '16 at 18:52
-
-
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);
Описал то что спрашивали ))
- 1,665
-
-
Функция 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
Внутри второй функции будет свой this, не имеющий ничего общего с this из "верхнего" кода. Для доступу к этому "верхнему" this мы и объявили переменную. Не найдя такой переменной js внутри функции, js полезет наверх, и упрётся в эту "глобальную"(в данном контексте) переменную self
- 1,317
-
-
@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
-
-
Я все же не до конца понимаю. Почему переменная 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
thisпри вызове функции как конструктора, 2. что такое замыкание (closure), где переменнаяselfдоступна в обработчикеonclick, athisв этом обработчике, отличается отthisвMenu. – Aug 05 '16 at 16:04