Глобальный объект

Глобальный объект создается при запуске среды JS и служит неявным контекстом языка, то есть контекстом, используемым, когда не предоставляется явный контекст. На практике это означает, что необъявленное присвоение переменной эффективно создает свойство глобального объекта:

foo = 1;
// equivalent to
window.foo = 1;

Точно так же, когда делается ссылка на нелокальную переменную, JS использует глобальный объект в качестве контекста:

foo; // 1
// same as
window.foo; // 1

Хотя неявно и явно глобальные переменные (последние объявлены с ключевым словом var в лексически глобальном пространстве) являются свойствами глобального объекта, только неявно глобальные переменные могут быть удалены:

a = 5;
var b = 6;

delete a; // true
delete b; // false

a; // reference error
b; // 6

Изменяемость объекта

В отличие от примитивных типов, большинство объектов JS являются изменяемыми, то есть их можно изменять на месте.

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

Область видимости и контекст при выполнении функции

Выполнение функции происходит в два этапа: на первом объявляются все переменные (внутренняя область действия, параметры/аргументы и глобальные) и функции в области видимости (это связано с феноменом JS подъем) и определяется значение this, а во втором код выполняется построчно, и значения присваиваются переменным по мере прохождения этого процесса. Подробнее об этом процессе здесь.

Контекст — это то, на что ссылается ключевое слово this. Более подробно он описан ниже. Контекст можно рассматривать как объект, который «владеет» исполняемым в данный момент кодом.

Область может быть либо функциональной, либо глобальной (хотя ECMA6 также представила блочную область действия с let) и относится к областям кода, в которых данный идентификатор (например, имя переменной) доступен для ссылки. .

Контекст объекта и ключевое слово this

В JavaScript this — это текущий контекст выполнения функции. Контекст определяется в зависимости от типа вызова: функция, метод, конструктор или косвенный вызов.

Вызов функции

При вызове функции (когда за выражением, вычисляющим значение Function, следуют круглые скобки) this относится к глобальному объекту или, в строгом режиме, undefined:

(function() {
  console.log(this);
})(); // Window {...}

Вызов метода

При вызове метода (когда функция, хранящаяся как свойство в объекте, вызывается с помощью метода доступа к свойству) this относится к объекту, которому принадлежит метод:

var obj = {
  func: function() {
    console.log(this);
  }
};

obj.func(); // Object {func: function}

Косвенный вызов

При косвенном вызове (когда функция вызывается с использованием call или apply) this относится к объекту, переданному в качестве аргумента контекста.

var a = "I'm a global property!"

function func() {
  console.log(this.a);
}

var obj = {
  a: "I'm obj's property!"
};

func(); // I'm a global property!
func.call(obj); // I'm obj's property!

Связанные функции

Когда контекст привязан к функции с bind, эта функция будет всегда ссылаться на аргумент как на контекст:

function func() {
  console.log(this.a);
}

obj = {
  a: "Hello"
}

var boundFunc = func.bind(obj);

var a = "Goodbye";

boundFunc(); // Hello

Привязать, подать заявку, позвонить

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

вызов

call позволяет нам вызывать функцию с явным контекстом, а также может дополнительно предоставлять аргументы функции в виде элементов, разделенных запятыми:

function simpleFunc() {
  console.log(this.a);
}

var obj = {
  a: "Hello"
};

simpleFunc.call(obj); // Hello

function complexFunc(arg) {
  console.log(this.a + ', ' + arg + '!');
}

complexFunc.call(obj, "world"); // Hello, world!

применять

apply идентичен call за исключением того, что необязательные аргументы функции предоставляются в виде одного аргумента массива:

var obj = {
  a: "Hello"
};

var arguments = ['my', 'friend'];

function complexFunc(arg1, arg2) {
  console.log(this.a + ', ' + arg1 + ' ' + arg2 + '!');
}

complexFunc.apply(obj, arguments); // Hello, my friend!

связывать

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

var greeting = "Hello from global scope!";

function func() {
  console.log(this.greeting);
}

var obj = {
  greeting: "Hello from obj!"
};

var boundFunc = func.bind(obj);

boundFunc(); // Hello from obj!

NB: После установки с помощью bind контекст не может быть изменен, даже если связанная функция вызывается с помощью call или apply или добавляется к другому объекту в качестве свойства.

Закрытия

В Документации MDN замыкания описываются как комбинация функции и лексического окружения, в котором эта функция была объявлена. Другими словами, когда функция создается, она закрывает свою текущую область действия, делая все идентификаторы, доступные функции при создании, доступными ей где бы она ни выполнялась в коде. Это иллюстрируется следующей функцией counter:

function makeCounter() {
  var index = 0;
  return function counter() {
    index += 1;
    console.log(index);
  };
}

var c = makeCounter();

c(); // 1
c(); // 2
c(); // 3
c(); // 4
console.log(index) // ReferenceError

Несмотря на то, что c выполняется в глобальной области видимости (где index не отображается, как показано ReferenceError в последней строке), он сохраняет доступ к index через свое закрытие и, таким образом, может ссылаться на него.

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