Правила области видимости на любом языке имеют решающее значение для понимания контекста, в котором работает ваш код, и к каким переменным имеют доступ функции.

В JavaScript все находится внутри глобального объекта. Даже переменные, которые вы определяете, на самом деле являются просто параметрами глобального объекта. В браузере этим глобальным объектом будет окно.

Внутри глобального объекта каждая функция имеет свою собственную локально вложенную область видимости. Убедитесь, что при определении переменных в локальной области вы делаете это с помощью var, чтобы предотвратить переход к глобальной переменной.

// Global scope out here
var foo = function () {
 // Local scope in here
}

Это довольно типично для большинства языков программирования, но у Javascript есть некоторые особенности. Вы также можете получить доступ к глобальным переменным в вашей локальной области. Обычно такие языки, как Ruby, имеют специальную нотацию для обозначения глобальных переменных ($global), но в JS такого различия нет. Ей доступны все переменные, объявленные вне функции.

var global = “I am global”
var foo = function () {
 var local = “I am local”
 console.log(global)
 console.log(local)
}
>> “I am global”
>> “I am local”

Это называется лексическим охватом.

Лексический охват

Каждый раз, когда вы определяете функцию внутри функции, она может получить доступ к переменным функции, в которую она вложена. Это отличается от такого языка, как Ruby, функции которого не являются объектами первого класса и не имеют лексической области видимости (в отличие от замыканий — блоков и процедур).

Лексическая область видимости просто означает, что в цепочке поиска самая дальняя вложенная функция имеет доступ к переменным на всем пути вверх по цепочке до самого верха, но функции над ней не могут получить доступ к своим функциям. Это хорошо работает с типичным для JavaScript наследованием (подробнее об этом позже).

Доступ к переменным за пределами непосредственной области видимости — это поведение замыкания (подробнее об этом позже).

Что с этим не так?

Контекст неявной функции

это ссылка на контекст текущего объекта в лексической области видимости. Но это работает не так хорошо, как можно было бы ожидать. this относится не к текущей функции или объекту, в котором она содержится, а скорее к объекту или контексту, с которым она была вызвана.

var myFunction = function() {
 console.log(this); // this === the global object
};
myFunction(); // returns global object [object Window]
// this makes sense because myFunction() is equivalent to window.myFunction()
var myObject = {
 myMethod: function() {
 console.log(this); //this = Object {myObject}
 };
};

Никаких сюрпризов. Но что-то странное происходит, когда мы вкладываем функцию в функцию.

var test_context = {
 foo: function() {
 console.log(this);
 }
};
object.foo(); // this is object test_context
var bar = object.foo;
bar(); // this object Window

Почему изменился контекст для вызова функции? Когда мы переназначили метод bar, мы не взяли с собой объект. Просто функция. Теперь, когда он вызывается, он привязывается к контексту глобального объекта. Привязка происходит, когда функция выполняется — не определена.

Явный контекст функции

Есть несколько способов минимизировать потерю контекста для функций. Мы можем использовать методы call, apply и bind.

foo = function(){
 console.log(this);
};
foo.call(object); // calls a function with a specific object as the context would log object
foo.apply(object, []); // same as call
foo.bind(object); // permanently binds object to foo so that whenever it is called it is called within that context.

Потеря контекста может быть очень запутанной, когда вы начинаете передавать вещи, но следует помнить, что привязка контекста происходит, когда функция вызывается или выполняется, а не когда она определена.

На следующей неделе я более подробно остановлюсь на замыканиях и потере контекста.