Контекст вызова
Что такое контекст?
Контекст - это значение ключевого слова this, которое является ссылкой на объект, который «владеет» текущим исполняемым кодом или функцией, на которую он смотрит.

Мы знаем, что window является глобальным объектом в браузере, поэтому, если мы введем его в консоли, он должен вернуть объект окна, что он и сделает.
Примечание
значение ключевого слова this зависит от объекта, на котором функция запускается / вызывается / работает.
Поэтому ключевое слово this имеет разные значения в зависимости от того, где оно используется.
Примечание
this и context взаимозаменяемы.
Контекст - глобально и внутри функции.
1function foo(){2console.log('foo context',this)3}
foo - это функция, определенная на глобальном уровне и вызываемая на объекте глобального уровня, то есть window, поэтому вызов foo и window.foo одинаков.
Следовательно, контекст - это объект window.
Если мы вызовем foo с помощью ключевого слова new т.e new foo () на глобальном уровне, то получим это как объект foo.

Примечание
Оператор new создает экземпляр объекта. Контекст функции будет установлен на созданный экземпляр объекта.
Контекст - для функций 2-го уровня
1var person = {2 name:'John',3 me:function(){4 return this5 },6 birthday:{7 day:11,8 my:function(){9 return this10 }11 }12 }1314console.log(person.me() === person) // true1516console.log(person.birthday.my() === person) // false1718console.log(person.birthday.my() === person.birthday) //true
Неявное связывание (Implicit Binding)
Когда функция определяется глобально и используется в рамках объекта.
1function foo(){2 return this3}45var obj = {6 method:foo7}89console.log(obj.method() === obj) //true10console.log(obj.method() === window) // false
Из вышеописанного мы получаем, что значение this зависит от вызываемой функции, а не от того, где функция определена.
Как контекст ведет себя в «use strict»?
При использовании use strict в функции контекст, т.e this, ведет себя иначе.
Контекст остается тем, к чему он был призван.
1function foo(){2 'use strict'3 return this;4}56console.log(foo() === window) /// false7console.log(foo() === undefined) /// true8console.log(window.foo() === window) /// true
Как контекст ведет себя в контексте выполнения (execution context)?
Контекст выполнения - это «среда» или область действия, в которой выполняется функция. Каждый раз, когда вызывается функция, создается новый контекст выполнения. Каждый вызов контекста выполнения имеет 2 этапа
- Создание - при вызове функции
- Активация - при выполнении функции
Значение this определяется на этапе создания, а не во время выполнения.
Однако правило определения this остается прежним.
Чем контекст отличается от области видимости?
Области видимости и контекст - это совершенно разные концепции, но обычно они используются как взаимозаменяемые. Область видимости - это доступность переменных, функций или объектов в определенной части вашего кода во время выполнения. Подробнее об области видимости здесь.
Важно
Каждый вызов функции имеет как область видимости, так и связанный с ним контекст.
Как явно изменить контекст? (explicitly binding)
Мы можем динамически изменять контекст любого метода, используя методы call, apply и bind.
Call - первый аргумент, который принимает call, - это контекст, который вы хотите использовать. После этого вы можете передать любое количество значений, разделенных запятыми.
1foo.call(context, param1, param2, param3 );
1function sayName(){2return this.name3}45console.log("my name is", sayName.call({name:"John"}))6console.log("my name is", sayName.call({name:"Bob"}))78// С параметрами910function sayFullName(lastName){11 return this.name + lastName12}1314console.log("my fullname is", sayFullName.call({name:"John"},'Doe'))15console.log("my fullname is", sayFullName("Cruz"))
Apply - то же самое, что и call, но фундаментальное различие между ними заключается в том, что функция call() принимает список аргументов, в то время как функция apply() принимает единичный массив аргументов.
1func.apply( context,[param1, param2, param3]);
1const person = {2 firstName: 'John',3 lastName: 'Doe'4}56function greet(greeting, message) {7 return `${greeting} ${this.firstName}. ${message}`;8}9const result = greet.apply(person, ['Hello', 'How are you?']);1011console.log(result); // Hello John. How are you?
Bind - возвращает новую функцию, которая навсегда привязана к первому аргументу bind, независимо от того, как функция используется.
bind не вызывает связанную функцию сразу, а возвращает новую функцию, которую мы можем запустить позже.
1const person = {2 firstName: 'John',3 lastName: 'Doe'4}56function greet(greeting, message) {7 return `${greeting} ${this.firstName}. ${message}`;8}9const result = greet.apply(person, ['Hello', 'How are you?']);1011console.log(result); // Hello John. How are you?121314var greatPerson = greet.bind(person)1516console.log(greatPerson("Hello","How are you?"))17console.log(greatPerson("Hello","How is going?"))18console.log(greatPerson("Hello","What do you think?"))
Важно
call/applyвызывает функцию с заданным контекстом, аbind, создаёт "обёртку" над функцией, которая подменяет контекст этой функции.bind- не вызывает функцию, а лишь возвращает "обёртку", которую можно вызвать позже.call/apply- вызывает функцию сразу и возвращает ее результат.call/apply- позднее связывание,bind- раннее связывание.
Зачем нам нужно явно менять контекст? 🤔
Когда нам нужно вызвать функцию, определенную внутри объекта, скажем,
x, но на других объектах,скажемy, используя явные методы привязки контекста для этого, чтобы увеличить возможность повторного использования функций.Каррирование и частичное применение - это еще одна часть, в которой используется явное изменение контекста.
Чтобы сделать служебные функции вроде
1function findMax(arr){2 return Math.max.apply(null,arr)3}45console.log(findMax([1,11,21,22,44,2,33])) // 44
- Наследование - это еще одно место, где можно использовать явное изменение контекста.
В каких случаях нам нужно учитывать контекст?
Мы можем потерять контекст, т.е. получить для него неопределенное значение.
- Вложенные функции
1var obj ={2 f1:function(){},3 f2:function(callback){4 callback()5 },6 exec:function(){7 this.f2(function callback(){8 console.log('this',this)9 this.f1()10 })11 }12}1314obj.exec()
Результат:

Нам нужно сохранить контекст объекта obj, на который ссылается функция callback, когда вызывается как описано выше.тк не происходит, и мы получаем ошибку.
Мы можем избавиться от указанной выше ошибки, заменив код exec на приведенный ниже
1// bind method2exec: function () {3 this.f2(function () {4 this.f1();5 }.bind(this));6}78//9exec: function () {10 var that = this;11 this.f2(() => {12 that.f1();13 });14}15}
Важно
Любая ссылка на функцию (присвоение значения, передача в качестве аргумента) теряет исходную привязку функции.
Практика 👨💻👩💻
Задание :one:
Создать связанные копии printFullName и printDetails.
1var person = {2 firstName : "John",3 lastName : "Smith",4 age : 235};67function printFullName()8{9 console.log(this.firstName + " " + this.lastName);10}1112function printDetails()13{14 console.log(this.firstName + " is " + this.age + " years old");15}1617var boundPrintFullName;18var boundPrintDetails;1920boundPrintFullName();21boundPrintDetails();
Задание :two:
Создайте объект calc с тремя методами
- read() запрашивает
promptдва значения и сохраняет их как свойства объекта; - sum() возвращает сумму двух значений;
- multiply() возвращает произведение двух значений.
1var calc = {2 sum: function() {3 //4 },56 multiply: function() {7 //8 },910 read: function() {11 //12 }13}1415calc.read();16console.log(calc.sum());17console.log(calc.multiply());