По просьбам некоторых читателей решил написать топик про контекст в javascript. Новички javascript часто не понимают значение ключевого слова this в javascript. Данный топик будет интересен не только новичкам, а также тем, кто просто хочет освежить данный аспект в памяти. Посмотрите пример ниже. Если вы затрудняетесь ответить на вопрос «что будет выведено в логе» хотя бы в одном из пунктов или хотите просто посмотреть ответы — добро пожаловать под кат.
В отличие от многих других языков программирования ключевое слово this в javascript не привязывается к объекту, а зависит от контекста вызова. Для упрощения понимания будем рассматривать примеры применительно к браузеру, где глобальным объектом является window.
В данном случае this внутри функции f равен глобальному объекту (например, в браузере это window, в Node.js — global).
Самовызывающиеся функции (self-invoking) работают по точно такому же принципу.
При вызове функции с использованием ключевого слова new функция выступает в роли конструктора, и в данном случе this указывает на создаваемый объект.
Если функция запускается как свойство объекта, то в this будет ссылка на этот объект. При этом не имеет значения, откуда данная функция появилась в объекте, главное — как она вызывается, а именно какой объект стоит перед вызовом функции:
Методы apply и call позволяют задать контекст для выполняемой функции. Разница между apply и call — только в способе передачи параметров в функцию. Первый параметр обеих функций определяет контекст выполнения функции (то, чему будет равен this).
Примеры:
Похитрее:
Применим полученные знания к приведенной в начале топика задаче. Опять же для упрощения будем рассматривать примеры применительно к браузеру, где глобальным объектом является window.
Результат: 3
Результат: 5
Результат: 4
Результат: undefined
Результат: 5
Результат: 5
Внимание: Если данный пример рассматрив��ть отдельно от остальных, то в логе будет не 5, а undefined. Попробуйте внимательно разобрать пример и объяснить поведение
В статье я постарался описать, как работает ключевое слово this в javascript. В ближайшее время я скорее всего напишу статью, в которой описывается для чего нужно знать эти тонкости (например, jQuery.proxy)
P.S. Если вы заметили ошибки/неточности или хотите что-то уточнить/добавить — напишите в ЛС, поправлю.
var f = function() { this.x = 5; (function() { this.x = 3; })(); console.log(this.x); }; var obj = {x: 4, m: function() { console.log(this.x); }}; f(); new f(); obj.m(); new obj.m(); f.call(f); obj.m.call(f);
Ответы, для нетерпеливых
3
5
4
undefined
5
5
5
4
undefined
5
5
1. Теория
В отличие от многих других языков программирования ключевое слово this в javascript не привязывается к объекту, а зависит от контекста вызова. Для упрощения понимания будем рассматривать примеры применительно к браузеру, где глобальным объектом является window.
1.1. Простой вызов функции
function f() { console.log(this === window); // true } f();
В данном случае this внутри функции f равен глобальному объекту (например, в браузере это window, в Node.js — global).
Самовызывающиеся функции (self-invoking) работают по точно такому же принципу.
(function () { console.log(this === window); // true })();
1.2. В конструкторе
function f() { this.x = 5; console.log(this === window); // false } var o = new f(); console.log(o.x === 5); // true
При вызове функции с использованием ключевого слова new функция выступает в роли конструктора, и в данном случе this указывает на создаваемый объект.
1.3. В методе объекта
var o = { f: function() { return this; } } console.log(o.f() === o); // true
Если функция запускается как свойство объекта, то в this будет ссылка на этот объект. При этом не имеет значения, откуда данная функция появилась в объекте, главное — как она вызывается, а именно какой объект стоит перед вызовом функции:
var o = { f: function() { return this; } } var o2 = {f: o.f}; console.log(o.f() === o);//true console.log(o2.f() === o2);//true
1.4. Методы apply, call
Методы apply и call позволяют задать контекст для выполняемой функции. Разница между apply и call — только в способе передачи параметров в функцию. Первый параметр обеих функций определяет контекст выполнения функции (то, чему будет равен this).
Разница в apply/call
function f(a,b,c) { return a * b + c; } f.call(f, 1, 2, 3); // аргументы перечисляются через запятую; var args = [1,2,3]; f.apply(f, args); // // аргументы передаются в виде массива; // В обоих случаях вызовется функция <b>f</b> с аргументами a = 1, b = 2, c = 3;
Примеры:
function f() { } f.call(window); // this внутри функции f будет ссылаться на объект window f.call(f); //this внутри f будет ссылаться на f
Похитрее:
function f() { console.log(this.toString()); // 123 } f.call(123); // this внутри функции f будет ссылаться на объект Number со значением 123
2. Разбираем задачу
Применим полученные знания к приведенной в начале топика задаче. Опять же для упрощения будем рассматривать примеры применительно к браузеру, где глобальным объектом является window.
2.1. f()
var f = function() { // Функция f вызывается с помощью простого вызова - f(), // поэтому this ссылается на глобальный объект this.x = 5; // window.x = 5; // В пункте 1.1 также указано, что в самовызывающихся функциях this также ссылается на глобальный объект (function() { this.x = 3; // window.x = 3 })(); console.log(this.x); // console.log(window.x) };
Результат: 3
2.2. new f();
var f = function() { // Функция f вызывается с использованием ключевого слова new, // поэтому this ссылается на создаваемый объект (обозначим его как object) this.x = 5; // object.x = 5; // В пункте 1.1 также указано, что в самовызывающихся функциях this ссылается на глобальный объект (function() { this.x = 3; // window.x = 3 })(); console.log(this.x); // console.log(object.x) };
Результат: 5
2.3. obj.m();
var obj = {x: 4, m: function() { // из пункта 1.3 следует, что this === obj, console.log(this.x); // console.log(obj.x) } };
Результат: 4
2.4. new obj.m();
var obj = {x: 4, m: function() { // из пункта 1.2 следует, что this ссылается на вновь создаваемый объект // но внутри данной функции не устанавливается значение для this.x, поэтому результат - undefined console.log(this.x); } };
Результат: undefined
2.5. f.call(f);
var f = function() { // Функция f вызывается с помощью метода call // первым параметром в call указана сама функция (точнее объект) f, поэтому // поэтому this ссылается на f this.x = 5; // f.x = 5; // В пункте 1.1 также указано, что в самовызывающихся функциях this ссылается на глобальный объект (function() { this.x = 3; // window.x = 3 })(); console.log(this.x); // console.log(f.x) };
Результат: 5
2.6. obj.m.call(f);
var obj = {x: 4, m: function() { // функция вызвана с помощью метода call // первым аргументом указана функция f // поэтому this вновь ссылается на f // в предыдущем примере f.x было присвоено значение 5, поэтому результат 5 console.log(this.x); // console.log(f.x) } };
Результат: 5
Внимание: Если данный пример рассматрив��ть отдельно от остальных, то в логе будет не 5, а undefined. Попробуйте внимательно разобрать пример и объяснить поведение
var f = function() { this.x = 5; (function() { this.x = 3; })(); console.log(this.x); }; var obj = {x: 4, m: function() { console.log(this.x); }}; obj.m.call(f);
Вместо заключения
Ответы все вместе
3
5
4
undefined
5
5
5
4
undefined
5
5
В статье я постарался описать, как работает ключевое слово this в javascript. В ближайшее время я скорее всего напишу статью, в которой описывается для чего нужно знать эти тонкости (например, jQuery.proxy)
P.S. Если вы заметили ошибки/неточности или хотите что-то уточнить/добавить — напишите в ЛС, поправлю.
