По просьбам некоторых читателей решил написать топик про контекст в 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. Если вы заметили ошибки/неточности или хотите что-то уточнить/добавить — напишите в ЛС, поправлю.