Functions

Контексты исполнения
Контекст исполнения (Execution context) – это абстрактное понятие, используемое спецификацией ECMA, для типизации и разграничения исполняемого кода.Стандарт не задаёт чётких рамок на структуру и вид EC с точки зрения реализации; это задача JavaScript-движков, реализующих стандарт.
Объект переменных(Variable object, VO) - это связанный с контекстом исполнения объект, служащий хранилищем для:
-
переменных (var);
-
деклараций функций (FunctionDeclaration, сокращённо FD);
-
и формальных параметров функции,
var a = 10;
function test(x) {
var b = 20;
};
test(30);// Объект переменных глобального контекста
VO(globalContext) = {
a: 10,
test: <ссылка на функцию>
};
// Объект переменных контекста функции test
VO(test functionContext) = {
x: 30,
b: 20
};Глобальный объект (Global object) — объект, который создаётся до входа в любой из контекстов исполнения. Данный объект существует в единственном экземпляре, свойства его доступны из любого места программы, жизненный цикл объекта завершается с завершением программы.
При создании, глобальный объект инициализируется такими свойствами, как Math, String, Date, parseInt и т.д., а также, дополнительными объектами, среди которых может быть и ссылка на сам глобальный объект — например, в BOM, свойство window глобального объекта ссылается на сам глобальный объект (однако, не во всех реализациях):
global = {
Math: <...>,
String: <...>
...
...
window: global
};В глобальном контексте, объектом переменных является сам глобальный объект
VO(globalContext) === global
var a = new String('test');
alert(a); // напрямую, будет найдено в VO(globalContext): "test"
alert(window['a']); // косвенно через global === VO(globalContext): "test"
alert(a === this.a); // true
var aKey = 'a';
alert(window[aKey]); // косвенно, имя свойства сформировано налету: "test"Объект активации (Activation object, AO) — специальный объект, который создаётся при входе в контекст функции и инициализируется свойством arguments — Объект аргументов (Arguments object):
AO = {
arguments: <ArgO>
};Объект аргументов (Arguments object, ArgO) – объект, находящийся в объекте активации контекста функции и содержащий следующие свойства:
- callee – ссылка на выполняемую функцию;
-
length – количество реально переданных параметров;
- свойства-индексы (числовые, приведённые к строке), значения которых – есть формальные параметры функции (слева направо в списке параметров). Количество этих свойств-индексов == arguments.length. Значения свойств-индексов объекта arguments и присутствующие формальные параметры – взаимозаменяемы:
function foo(x, y, z) {
alert(foo.length);
alert(arguments.length);
alert(arguments.callee === foo);
alert(x === arguments[0]);
alert(x); // 10
arguments[0] = 20;
alert(x); // 20
x = 30;
alert(arguments[0]);
z = 40;
alert(arguments[2]);
arguments[2] = 50;
alert(z); // 40
}
foo(10, 20);Что выведет
каждый alert?
function foo(x, y, z) {
// количество описанных параметров функции (x, y, z)
alert(foo.length); // 3
// количество реально переданных параметров (только x, y)
alert(arguments.length); // 2
// ссылка функции на саму себя
alert(arguments.callee === foo); // true
// разделение параметров
alert(x === arguments[0]); // true
alert(x); // 10
arguments[0] = 20;
alert(x); // 20
x = 30;
alert(arguments[0]); // 30
// однако, для не переданного параметра z,соответствующее
//свойство-индекс объекта arguments - не взаимозаменяемое
z = 40;
alert(arguments[2]); // undefined
arguments[2] = 50;
alert(z); // 40
}
foo(10, 20);Детализация обработки кода контекста
Обработка кода контекста исполнения делится на два основных этапа:
-
Вход в контекст исполнения;
-
Непосредственно, интерпретация кода.
Обработка этих двух стадий является общей для всех контекстов.
При входе в контекст исполнения (но до построчного выполнения его кода), VO наполняется следующими свойствами:
* для каждого формального параметра функции (если мы находимся в контексте исполнения функции)
– создаётся свойство VO с именем и значением формального параметра; для непереданных параметров – создаётся свойство VO с именем формального параметра и значением undefined;
* для каждой декларации функции (FunctionDeclaration, FD)
– создаётся свойство VO, с именем функции и значением, являющимся ссылкой на объект-функцию; если в VO уже присутствовало свойство с таким именем, оно его значение и атрибуты заменяются значением функции;
* для каждой переменной (var)
– создаётся свойство VO с именем переменной, и значением undefined; если в VO уже присутствовало свойство с таким именем, оно остаётся нетронутым.
( является общей для всех контекстов.)
Чем будет наполнен АО?
function test(a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
test(10); // вызовAO(test) = {
a: 10,
b: undefined,
c: undefined,
d: <ссылка на FunctionDeclaration "d">
e: undefined
};В AO не попала функция “x”. Это потому, что “x” является не декларацией функции, а функцией-выражением (FunctionExpression, сокращённо FE), которые не воздействуют на VO.
Интерпретация кода
function test(a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
test(10); // вызовAO(test) = {
a: 10,
b: undefined,
c: 10,
d: <ссылка на FunctionDeclaration "d">
e: <ссылка на FunctionExpression "_e">
};alert(x);
var x = 10;
alert(x);
x = 20;
function x() {}
alert(x);alert(x); // function
var x = 10;
alert(x); // 10
x = 20;
function x() {}
alert(x); // 20if (true) {
var a = 1;
} else {
var b = 2;
}
alert(a);
alert(b);if (true) {
var a = 1;
} else {
var b = 2;
}
alert(a); // 1
alert(b);
// undefined,
// но не "b is not defined"This
Является свойством контекста исполнения:
activeExecutionContext = {
VO: {...},
this: thisValue
};где VO — это объект переменных (variable object). Значение this напрямую связанно с типом исполняемого кода контекста. Определяется оно при входе в контекст и на протяжении исполнения кода контекста, является неизменным.
This в глобальном контексте
В коде глобального контекста, значением this всегда является сам глобальный объект (global)
this.a = 10;
alert(a);
b = 20;
alert(this.b);
var c = 30;
alert(this.c); // явное объявление свойства
// глобального объекта
this.a = 10; // global.a = 10
alert(a); // 10
// косвенное, посредством присваивания
// неопределённому до этого идентификатору
b = 20;
alert(this.b); // 20
// также косвенное, посредством объявления
// переменной, поскольку объектом переменных
// в глобальном контексте является сам глобальный объект
var c = 30;
alert(this.c); // 30This в коде функции
Особенность значения this в этом типе кода заключается в том, что оно не связано статично с функцией
This определяется при входе в контекст, и в случае с кодом функции, каждый раз может быть абсолютно разным.
!!! На протяжении исполнения кода контекста, значение this является неизменным, т.е. нельзя присвоить ему новое значение динамически в рантайме
Что выведет каждый alert?
var foo = {x: 10};
var bar = {
x: 20,
test: function () {
alert(this === bar);
alert(this.x);
this = foo;
alert(this.x);
}
};
bar.test();
foo.test = bar.test;
foo.test(); var foo = {x: 10};
var bar = {
x: 20,
test: function () {
alert(this === bar); // true
alert(this.x); // 20
this = foo; // ошибка, нельзя менять this
alert(this.x); // если бы не было ошибки, было бы 10, а не 20
}
};
// при входе в контекст, значение this
// определёно как объект "bar"; почему – будет
// подробно разобрано ниже
bar.test(); // true, 20
foo.test = bar.test;
// однако, здесь уже this указывает
// на "foo" - при вызове той же функции
foo.test(); // false, 10От чего же зависит меняющееся значение this в коде функции?
При обычном вызове функции, this определяется вызывающей стороной, которая активирует код контекста функции, — так называемый, caller, т.е. родительский контекст, который вызывает функцию. Определение значения this происходит по форме выражения вызова (иными словами, как синтаксически вызвана функция).
Что выведет каждый alert?
var foo = {
bar: function () {
alert(this);
alert(this === foo);
}
};
foo.bar();
var exampleFunc = foo.bar;
alert(exampleFunc === foo.bar);
exampleFunc(); var foo = {
bar: function () {
alert(this);
alert(this === foo);
}
};
foo.bar(); // foo, true
var exampleFunc = foo.bar;
alert(exampleFunc === foo.bar); // true
// опять же, при иной форме вызова той же
// функции, this уже установлен в другое значение
exampleFunc(); // global, falseThis при вызове функции в качестве конструктора
function A() {
alert(this);
this.x = 10;
}
var a = new A();
alert(a.x);function A() {
alert(this); // вновь сознанный объект, ниже - объект "a"
this.x = 10;
}
var a = new A();
alert(a.x); // 10Оператор new вызовет внутренний метод [[Construct]]функции A, который, в свою очередь, после создания объекта, вызовет внутренний метод [[Call]], всё той же функции А, передав в качестве значения this вновь созданный объект.
Явная установка значения this при вызове функций (apply и call)
Оба они принимают в качестве первого параметра значение this, которое будет использовано в контексте вызова. Разница между этими методами несущественная: для первого из них вторым параметром обязательно должен быть массив (либо массиво-подобный объект, например, arguments), в свою очередь, call может принимать любые параметры. Обязательным параметром для обоих методов является лишь первый — значение this
Что выведет каждый alert?
var b = 10;
function a(c) {
alert(this.b);
alert(c);
}
a(20);
a.call({b: 20}, 30);
a.apply({b: 30}, [40]) var b = 10;
function a(c) {
alert(this.b);
alert(c);
}
a(20); // this === Global, this.b == 10, c == 20
a.call({b: 20}, 30);
// this === {b: 20}, this.b == 20, c == 30
a.apply({b: 30}, [40])
// this === {b: 30}, this.b == 30, c == 40Scope, Scope chain
var x = 10;
function foo() {
var y = 20;
function bar() {
alert(x + y);
}
return bar;
}
foo()(); var x = 10;
function foo() {
var y = 20;
function bar() {
alert(x + y);
}
return bar;
}
foo()(); // 30Цепь областей видимости (Scope, Scope chain, сокращённо SC) – это связанная с контекстом исполнения цепь объектов переменных, в которой происходит поиск переменных при разрешении имён идентификаторов.
Scope chain функции создаётся при её выполнении, и состоит из объекта активации и внутреннего свойства функции [[Scope]].
activeExecutionContext = {
VO: {...}, // или AO
this: thisValue,
Scope: [ // Scope chain
// список всех объектов переменных
// для поиска идентификаторов
]
};[[Scope]] – это иерархическая цепь объектов переменных (VO), стоящих выше контекста функции; цепь записывается свойством в функцию при её создании (раз, и навсегда (до уничтожения функции). Т.е. функция может быть ни разу не вызвана, но свойство [[Scope]] в неё уже записано)
Замыкания (Closures) напрямую связаны со свойством функций [[Scope]]. Собственно, замыкание — это и есть комбинация кода функции и её свойства [[Scope]]. При этом, [[Scope]] одним из объектов цепи содержит то лексическое окружение (родительский объект переменных), в котором функция порождается. Переменные из вышестоящих контекстов при дальнейшей активации функции будут искаться именно в этой лексической (статически запомненной при создании) цепи объектов переменных.
Что выведет alert?
var x = 10;
function foo() {
alert(x);
}
(function () {
var x = 20;
foo();
})();var x = 10;
function foo() {
alert(x);
}
(function () {
var x = 20;
foo(); // 10, а не 20
})();function foo() {
var x = 10;
var y = 20;
return function () {
alert([x, y]);
};
}
var x = 30;
var bar = foo();
bar(); function foo() {
var x = 10;
var y = 20;
return function () {
alert([x, y]);
};
}
var x = 30;
var bar = foo(); // возвратилась анонимная функция
bar(); // [10, 20]Что выведет каждый alert?
function foo() {
alert(x);
}
Object.prototype.x = 10;
foo();function foo() {
alert(x);
}
Object.prototype.x = 10;
foo(); // 10function foo() {
var x = 20;
function bar() {
alert(x);
}
bar();
}
Object.prototype.x = 10;
foo();function foo() {
var x = 20;
function bar() {
alert(x);
}
bar();
}
Object.prototype.x = 10;
foo(); // 20Что выведет каждый alert?
var a = 10;
function testFn() {
alert(a);
}
(function (funArg) {
var a = 20;
funArg();
})(testFn);var a = 10;
function testFn() {
alert(a);
}
(function (funArg) {
var a = 20;
// "a" для funArg запомнилось статически из
// порождаемого её (лексического) контекста,
// поэтому:
funArg(); // 10, а не 20
})(testFn);var firstClosure;
var secondClosure;
function testFn() {
var a = 1;
firstClosure = function (){return ++a;};
secondClosure = function (){return --a;};
a = 2;
alert(firstClosure());
}
testFn();
alert(firstClosure());
alert(secondClosure());var firstClosure;
var secondClosure;
function testFn() {
var a = 1;
firstClosure = function () { return ++a; };
secondClosure = function () { return --a; };
a = 2; // воздействие на VO["a"], который в [[Scope]] замыканий
alert(firstClosure()); // 3, через firstClosure.[[Scope]]
}
testFn();
alert(firstClosure()); // 4
alert(secondClosure()); // 3var data = [];
for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
data[0]();
data[1]();
data[2]();Что выведет каждый alert?
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
data[0](); // 3, а не 0
data[1](); // 3, а не 1
data[2](); // 3, а не 2var data = [];
for (var k = 0; k < 3; k++) {
data[k] = (function _helper(x) {
return function () {
alert(x);
};
})(k);
}
data[0]();
data[1]();
data[2](); var data = [];
for (var k = 0; k < 3; k++) {
data[k] = (function _helper(x) {
return function () {
alert(x);
};
})(k); // передаём "k"
}
// теперь всё в порядке
data[0](); // 0
data[1](); // 1
data[2](); // 2Q & A
Functions
By Anna Protasevich
Functions
Scope, context, VO, closer
- 1,133