Открытый бесплатный вебинар об ООП в JavaScript
Тема "Методы объектов и контекст вызова" в учебнике Кантора казалась мне невероятно сложной и непонятной. Сергей Мелюков сумел объяснить её очень доступно и доходчиво. у человека определённо талант, такое понятное изложение сложного материала встречается не часто.
https://www.youtube.com/watch?v=5l01s6Vkqp0
This
Не из которого вызывается, а в контексте которого вызывается. Это важно.
Чаще всего эти понятия совпадают, но не всегда.
Контекст вызова функции можно изменить и тогда функция будет вызываться из одного объекта, но указывать при этом на другой объект.
Пример:
var a = {
prop: "a",
fn: function () {
return this.prop;
}
};
var b = {
prop: "b",
fn: function () {
return this.prop;
}
};
a.fn() // "a"
Пока всё просто: есть два объекта а и b, у каждого своя переменная prop с таким же значением и функция, которая this.prop возвращает. Ожидаемо a.fn() возвращает "a", b.fn() возвращает "b". В данном случае контекст вызова совпадает с объектом, из которого функция вызывается.
Перепишем вызов функций по-другому
a.fn.bind(b)() // "b"
b.fn.bind(a)() // "a"
Теперь a.fn.bind(b)() возвращает "b", b.fn.bind(a)() возвращает "a". Мы изменили контекст вызова.
Можно сделать иначе:
a.fn = a.fn.bind(b)
a.fn() // "b"
Работает.
А если сделать так?
a.fn = b.fn;
a.fn() // "a"
Не работает.Мы присвоили функции a.fn ссылку на функцию b.fn, но как только вызвали её из объекта a, контекст изменился на a. Поэтому для смены контекста применяют специальные методы, одним из которых является bind.
Ещё один момент: сменить контекст вызова можно только один раз:
var newFn = a.fn.bind(b);
newFn = newFn.bind(a);
newFn() // "b"
У функции, которую получили в результате работы bind, мы больше не можем сменить контекст. Контекст вызова можно изменить только один раз.
В повторном вызове bind есть ещё один эффект: bind способен накапливать аргументы.
К примеру, нам нужно вызвать функцию summ с двумя аргументами, мы вызываем через bind с одним аргументом, получаем ошибку вычисления. Вызываем ещё раз через bind и снова с одним аргументом, всё считает как нужно.
Смена контекста
Изменить контекст вызова функции позволяют три метода: bind(), call() и apply(). Эти методы очень похожи между собой, отличия незначительные. Каждый из них принимает первым параметром объект из которого вызывается функция или null и аргументы, которые в неё должны быть переданы.
Методы bind, call, apply
Контекст функции
(в порядке увеличения приоритета)
- функция вызывается из объекта
через точку a.f();
или через квадратные скобки a["f"](); - для вызова функции используются методы
bind
call
apply - функция вызывается с ключевым словом new
В браузере глобальный объект это window, в node.js - global.
Распространённая ошибка: часто считают, что если у внешней функции есть контекст, то и у вложенной в неё функции тоже будет контекст. На самом деле это не так. Контекст вложенной функции будет равен глобальному объекту.
var a = {
prop: "a",
f: function () {
var fn = function (){
console.log(this.prop);
};
fn();
}
};
a.f() // undefined
Если нужно передать контекст во внутреннюю функцию, можем использовать два варианта.
Первый вариант: используем методы bind, call,apply.
var a = {
prop: "a",
f: function () {
var fn = function (){
console.log(this.prop);
};
fn.call(this);
}
};
a.f() // a
Второй вариант: кеширование контекста. Для этого сохраняем значение this в переменную that и передаём его во внутреннюю функцию через замыкание.
var a = {
prop: "a",
f: function () {
var that = this; // название переменной that общепринятое для таких случаев
var fn = function (){
console.log( that.prop);
};
fn();
}
};
a.f() // a
l
// И всё-таки здесь есть вопрос. Если для вызова результата функции fn использовать не consoe.log, а return, результат будет undefined. Непонятно почему так.
Продолжение https://studyjavascript.blogspot.com/2019/03/blog-post_23.html