JavaScript â ÑзÑк Ñ ÑилÑнÑм ÑÑнкÑионалÑно-оÑиенÑиÑованнÑм Ñклоном. Ðн даÑÑ Ð½Ð°Ð¼ много ÑвободÑ. ФÑнкÑÐ¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸ÑеÑки Ñоздана, ÑкопиÑована в дÑÑгÑÑ Ð¿ÐµÑеменнÑÑ Ð¸Ð»Ð¸ пеÑедана как аÑгÑÐ¼ÐµÐ½Ñ Ð´ÑÑгой ÑÑнкÑии и позже вÑзвана из ÑовеÑÑенно дÑÑгого меÑÑа.
ÐÑ Ð·Ð½Ð°ÐµÐ¼, ÑÑо ÑÑнкÑÐ¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к пеÑеменнÑм из внеÑнего окÑÑжениÑ, ÑÑа возможноÑÑÑ Ð¸ÑполÑзÑеÑÑÑ Ð¾ÑÐµÐ½Ñ ÑаÑÑо.
Ðо ÑÑо пÑоизойдÑÑ, когда внеÑние пеÑеменнÑе изменÑÑÑÑ? ФÑнкÑÐ¸Ñ Ð¿Ð¾Ð»ÑÑÐ¸Ñ Ð¿Ð¾Ñледнее знаÑение или Ñо, коÑоÑое ÑÑÑеÑÑвовало на Ð¼Ð¾Ð¼ÐµÐ½Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÑÑнкÑии?
Ð ÑÑо пÑоизойдÑÑ, когда ÑÑнкÑÐ¸Ñ Ð¿ÐµÑемеÑÑиÑÑÑ Ð² дÑÑгое меÑÑо в коде и бÑÐ´ÐµÑ Ð²Ñзвана оÑÑÑда â полÑÑÐ¸Ñ Ð»Ð¸ она доÑÑÑп к внеÑним пеÑеменнÑм Ñвоего нового меÑÑоположениÑ?
РазнÑе ÑзÑки ведÑÑ ÑÐµÐ±Ñ Ð¿Ð¾-ÑÐ°Ð·Ð½Ð¾Ð¼Ñ Ð² ÑÐ°ÐºÐ¸Ñ ÑлÑÑаÑÑ , и в ÑÑой главе Ð¼Ñ ÑаÑÑмоÑÑим поведение JavaScript.
let/const здеÑÑÐ JavaScript ÑÑÑеÑÑвÑÐµÑ ÑÑи ÑпоÑоба обÑÑвиÑÑ Ð¿ÐµÑеменнÑÑ: let, const (ÑовÑеменнÑе), и var (пеÑежиÑок пÑоÑлого).
- Ð ÑÑой ÑÑаÑÑе Ð¼Ñ Ð±Ñдем иÑполÑзоваÑÑ Ð¿ÐµÑеменнÑе
letв пÑимеÑÐ°Ñ . - ÐеÑеменнÑе, обÑÑвленнÑе Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
const, ведÑÑ ÑÐµÐ±Ñ Ñак же, Ñак ÑÑо ÑÑа ÑÑаÑÑÑ Ð¸ о Ð½Ð¸Ñ . - СÑаÑÑе пеÑеменнÑе
varимеÑÑ Ð½ÐµÑколÑко Ñ Ð°ÑакÑеÑнÑÑ Ð¾ÑлиÑий, они бÑдÑÑ ÑаÑÑмоÑÑÐµÐ½Ñ Ð² главе УÑÑаÑевÑее клÑÑевое Ñлово "var".
Ðлоки кода
ÐÑли пеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¾Ð±ÑÑвлена внÑÑÑи блока кода {...}, Ñо она видна ÑолÑко внÑÑÑи ÑÑого блока.
ÐапÑимеÑ:
{
// вÑполнÑем некоÑоÑÑе дейÑÑÐ²Ð¸Ñ Ñ Ð»Ð¾ÐºÐ°Ð»Ñной пеÑеменной, коÑоÑÑе не Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ Ð²Ð¸Ð´Ð½Ñ ÑнаÑÑжи
let message = "Hello"; // пеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð²Ð¸Ð´Ð½Ð° ÑолÑко в ÑÑом блоке
alert(message); // Hello
}
alert(message); // ReferenceError: message is not defined
С помоÑÑÑ Ð±Ð»Ð¾ÐºÐ¾Ð² {...} Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ изолиÑоваÑÑ ÑаÑÑÑ ÐºÐ¾Ð´Ð°, вÑполнÑÑÑÑÑ ÑÐ²Ð¾Ñ ÑобÑÑвеннÑÑ Ð·Ð°Ð´Ð°ÑÑ, Ñ Ð¿ÐµÑеменнÑми, пÑинадлежаÑими ÑолÑко ей:
{
// показаÑÑ ÑообÑение
let message = "Hello";
alert(message);
}
{
// показаÑÑ Ð´ÑÑгое ÑообÑение
let message = "Goodbye";
alert(message);
}
ÐбÑаÑиÑе внимание, ÑÑо без оÑделÑнÑÑ Ð±Ð»Ð¾ÐºÐ¾Ð² Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÐµÑ Ð¾Ñибка, еÑли Ð¼Ñ Ð¸ÑполÑзÑем let Ñ ÑÑÑеÑÑвÑÑÑим именем пеÑеменной:
// показаÑÑ ÑообÑение
let message = "Hello";
alert(message);
// показаÑÑ Ð´ÑÑгое ÑообÑение
let message = "Goodbye"; // SyntaxError: Identifier 'message' has already been declared
alert(message);
ÐÐ»Ñ if, for, while и Ñ.д. пеÑеменнÑе, обÑÑвленнÑе в блоке кода {...}, Ñакже Ð²Ð¸Ð´Ð½Ñ ÑолÑко внÑÑÑи:
if (true) {
let phrase = "Hello";
alert(phrase); // Hello
}
alert(phrase); // ÐÑибка, Ð½ÐµÑ Ñакой пеÑеменной!
Ð ÑÑом ÑлÑÑае поÑле завеÑÑÐµÐ½Ð¸Ñ ÑабоÑÑ if нижний alert не ÑÐ²Ð¸Ð´Ð¸Ñ phrase, ÑÑо и пÑÐ¸Ð²ÐµÐ´ÐµÑ Ðº оÑибке.
Ð ÑÑо замеÑаÑелÑно, поÑколÑÐºÑ ÑÑо позволÑÐµÑ Ð½Ð°Ð¼ ÑоздаваÑÑ Ð±Ð»Ð¾Ñно-локалÑнÑе пеÑеменнÑе, оÑноÑÑÑиеÑÑ ÑолÑко к веÑви if.
То же Ñамое можно ÑказаÑÑ Ð¸ пÑо ÑÐ¸ÐºÐ»Ñ for и while:
for (let i = 0; i < 3; i++) {
// пеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ i видна ÑолÑко внÑÑÑи for
alert(i); // 0, поÑом 1, поÑом 2
}
alert(i); // ÐÑибка, Ð½ÐµÑ Ñакой пеÑеменной!
ÐизÑалÑно let i = 0; наÑ
одиÑÑÑ Ð²Ð½Ðµ блока кода {...}, однако здеÑÑ Ð² ÑлÑÑае Ñ for еÑÑÑ Ð¾ÑобенноÑÑÑ: пеÑеменнаÑ, обÑÑÐ²Ð»ÐµÐ½Ð½Ð°Ñ Ð²Ð½ÑÑÑи (...), ÑÑиÑаеÑÑÑ ÑаÑÑÑÑ Ð±Ð»Ð¾ÐºÐ°.
ÐложеннÑе ÑÑнкÑии
ФÑнкÑÐ¸Ñ Ð½Ð°Ð·ÑваеÑÑÑ Â«Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾Ð¹Â», когда она ÑоздаÑÑÑÑ Ð²Ð½ÑÑÑи дÑÑгой ÑÑнкÑии.
ÐÑо оÑÐµÐ½Ñ Ð»ÐµÐ³ÐºÐ¾ ÑделаÑÑ Ð² JavaScript.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ ÑÑо Ð´Ð»Ñ ÑпоÑÑдоÑÐ¸Ð²Ð°Ð½Ð¸Ñ Ð½Ð°Ñего кода, напÑимеÑ, как здеÑÑ:
function sayHiBye(firstName, lastName) {
// ÑÑнкÑиÑ-помоÑник, коÑоÑÑÑ Ð¼Ñ Ð¸ÑполÑзÑем ниже
function getFullName() {
return firstName + " " + lastName;
}
alert( "Hello, " + getFullName() );
alert( "Bye, " + getFullName() );
}
ÐдеÑÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ getFullName() Ñоздана Ð´Ð»Ñ ÑдобÑÑва. Ðна Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к внеÑним пеÑеменнÑм и, знаÑиÑ, вÑвеÑÑи полное имÑ. Ð JavaScript вложеннÑе ÑÑнкÑии иÑполÑзÑÑÑÑÑ Ð¾ÑÐµÐ½Ñ ÑаÑÑо.
ЧÑо еÑÑ Ð¸Ð½ÑеÑеÑнее, Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð²Ð¾Ð·Ð²ÑаÑена: либо в каÑеÑÑве ÑвойÑÑва нового обÑекÑа (еÑли внеÑнÑÑ ÑÑнкÑÐ¸Ñ ÑоздаÑÑ Ð¾Ð±ÑÐµÐºÑ Ñ Ð¼ÐµÑодами), либо Ñама по Ñебе. РзаÑем Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸ÑполÑзована в лÑбом меÑÑе. Ðе важно где, она вÑÑ Ñак же бÑÐ´ÐµÑ Ð¸Ð¼ÐµÑÑ Ð´Ð¾ÑÑÑп к Ñем же внеÑним пеÑеменнÑм.
Ðиже, makeCounter ÑÐ¾Ð·Ð´Ð°ÐµÑ ÑÑнкÑÐ¸Ñ Â«ÑÑÑÑÑик», коÑоÑÐ°Ñ Ð¿Ñи каждом вÑзове возвÑаÑÐ°ÐµÑ ÑледÑÑÑее ÑиÑло:
function makeCounter() {
let count = 0;
return function() {
return count++; // еÑÑÑ Ð´Ð¾ÑÑÑп к внеÑней пеÑеменной "count"
};
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2
ÐеÑмоÑÑÑ Ð½Ð° пÑоÑÑоÑÑ ÑÑого пÑимеÑа, немного модиÑиÑиÑованнÑе его ваÑианÑÑ Ð¿ÑименÑÑÑÑÑ Ð½Ð° пÑакÑике, напÑимеÑ, в генеÑаÑоÑе пÑевдоÑлÑÑайнÑÑ ÑиÑел и во Ð¼Ð½Ð¾Ð³Ð¸Ñ Ð´ÑÑÐ³Ð¸Ñ ÑлÑÑаÑÑ .
Ðак ÑÑо ÑабоÑаеÑ? ÐÑли Ð¼Ñ Ñоздадим неÑколÑко ÑÐ°ÐºÐ¸Ñ ÑÑÑÑÑиков, бÑдÑÑ Ð»Ð¸ они незавиÑимÑми дÑÑг Ð¾Ñ Ð´ÑÑга? ЧÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ñ Ð¿ÐµÑеменнÑми?
Ðонимание ÑÐ°ÐºÐ¸Ñ Ð²ÐµÑей полезно Ð´Ð»Ñ Ð¿Ð¾Ð²ÑÑÐµÐ½Ð¸Ñ Ð¾Ð±Ñего ÑÑÐ¾Ð²Ð½Ñ Ð²Ð»Ð°Ð´ÐµÐ½Ð¸Ñ JavaScript и Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ ÑложнÑÑ ÑÑенаÑиев. Так ÑÑо давайÑе немного ÑглÑбимÑÑ.
ÐекÑиÑеÑкое окÑÑжение
ÐлÑбокое ÑÐµÑ Ð½Ð¸ÑеÑкое опиÑание â впеÑеди.
Ðак Ð±Ñ Ð¼Ð½Ðµ ни Ñ Ð¾ÑелоÑÑ Ð¸Ð·Ð±ÐµÐ¶Ð°ÑÑ Ð½Ð¸Ð·ÐºÐ¾ÑÑовневÑÑ Ð´ÐµÑалей ÑзÑка, лÑбое пÑедÑÑавление о JavaScript без Ð½Ð¸Ñ Ð±ÑÐ´ÐµÑ Ð½ÐµÐ´Ð¾ÑÑаÑоÑнÑм и неполнÑм, Ñак ÑÑо пÑигоÑовÑÑеÑÑ.
ÐÐ»Ñ Ð±Ð¾Ð»ÑÑей наглÑдноÑÑи обÑÑÑнение ÑазбиÑо на неÑколÑко Ñагов.
Шаг 1. ÐеÑеменнÑе
Ð JavaScript Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ вÑполнÑемой ÑÑнкÑии, блока кода {...} и ÑкÑипÑа еÑÑÑ ÑвÑзаннÑй Ñ Ð½Ð¸Ð¼Ð¸ внÑÑÑенний (ÑкÑÑÑÑй) обÑекÑ, назÑваемÑй лекÑиÑеÑким окÑÑжением LexicalEnvironment.
ÐбÑÐµÐºÑ Ð»ÐµÐºÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ ÑоÑÑÐ¾Ð¸Ñ Ð¸Ð· двÑÑ ÑаÑÑей:
-
Environment Record â обÑекÑ, в коÑоÑом как ÑвойÑÑва Ñ ÑанÑÑÑÑ Ð²Ñе локалÑнÑе пеÑеменнÑе (а Ñакже некоÑоÑÐ°Ñ Ð´ÑÑÐ³Ð°Ñ Ð¸Ð½ÑоÑмаÑиÑ, ÑÐ°ÐºÐ°Ñ ÐºÐ°Ðº знаÑение
this). -
СÑÑлка на внеÑнее лекÑиÑеÑкое окÑÑжение â Ñо еÑÑÑ Ñо, коÑоÑое ÑооÑвеÑÑÑвÑÐµÑ ÐºÐ¾Ð´Ñ ÑнаÑÑжи (ÑнаÑÑжи Ð¾Ñ ÑекÑÑÐ¸Ñ ÑигÑÑнÑÑ Ñкобок).
«ÐеÑеменнаÑ» â ÑÑо пÑоÑÑо ÑвойÑÑво ÑпеÑиалÑного внÑÑÑеннего обÑекÑа: Environment Record. «ÐолÑÑиÑÑ Ð¸Ð»Ð¸ измениÑÑ Ð¿ÐµÑеменнÑÑ», ознаÑаеÑ, «полÑÑиÑÑ Ð¸Ð»Ð¸ измениÑÑ ÑвойÑÑво ÑÑого обÑекÑа».
ÐапÑимеÑ, в ÑÑом пÑоÑÑом коде ÑолÑко одно лекÑиÑеÑкое окÑÑжение:
ÐÑо, Ñак назÑваемое, глобалÑное лекÑиÑеÑкое окÑÑжение, ÑвÑзанное Ñо вÑем ÑкÑипÑом.
Ðа каÑÑинке вÑÑе пÑÑмоÑголÑник ознаÑÐ°ÐµÑ Environment Record (Ñ
ÑанилиÑе пеÑеменнÑÑ
), а ÑÑÑелка ознаÑÐ°ÐµÑ ÑÑÑÐ»ÐºÑ Ð½Ð° внеÑнее окÑÑжение. У глобалÑного лекÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ Ð½ÐµÑ Ð²Ð½ÐµÑнего окÑÑжениÑ, Ñак ÑÑо она ÑказÑÐ²Ð°ÐµÑ Ð½Ð° null.
Ðо меÑе вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð´Ð° лекÑиÑеÑкое окÑÑжение менÑеÑÑÑ.
ÐÐ¾Ñ Ð±Ð¾Ð»ÐµÐµ длиннÑй код:
ÐÑÑмоÑголÑники Ñ Ð¿Ñавой ÑÑоÑÐ¾Ð½Ñ Ð´ÐµÐ¼Ð¾Ð½ÑÑÑиÑÑÑÑ, как глобалÑное лекÑиÑеÑкое окÑÑжение изменÑеÑÑÑ Ð² пÑоÑеÑÑе вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð´Ð°:
- ÐÑи запÑÑке ÑкÑипÑа лекÑиÑеÑкое окÑÑжение пÑедваÑиÑелÑно заполнÑеÑÑÑ Ð²Ñеми обÑÑвленнÑми пеÑеменнÑми.
- ÐзнаÑалÑно они наÑ
одÑÑÑÑ Ð² ÑоÑÑоÑнии «Uninitialized». ÐÑо оÑобое внÑÑÑеннее ÑоÑÑоÑние, коÑоÑое ознаÑаеÑ, ÑÑо движок Ð·Ð½Ð°ÐµÑ Ð¾ пеÑеменной, но на нее нелÑÐ·Ñ ÑÑÑлаÑÑÑÑ, пока она не бÑÐ´ÐµÑ Ð¾Ð±ÑÑвлена Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
let. ÐÑо поÑÑи Ñо же Ñамое, как еÑли Ð±Ñ Ð¿ÐµÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð½Ðµ ÑÑÑеÑÑвовала.
- ÐзнаÑалÑно они наÑ
одÑÑÑÑ Ð² ÑоÑÑоÑнии «Uninitialized». ÐÑо оÑобое внÑÑÑеннее ÑоÑÑоÑние, коÑоÑое ознаÑаеÑ, ÑÑо движок Ð·Ð½Ð°ÐµÑ Ð¾ пеÑеменной, но на нее нелÑÐ·Ñ ÑÑÑлаÑÑÑÑ, пока она не бÑÐ´ÐµÑ Ð¾Ð±ÑÑвлена Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
- ÐоÑвлÑеÑÑÑ Ð¾Ð¿Ñеделение пеÑеменной
let phrase. У Ð½ÐµÑ ÐµÑÑ Ð½ÐµÑ Ð¿ÑиÑвоенного знаÑениÑ, поÑÑÐ¾Ð¼Ñ Ð¿ÑиÑваиваеÑÑÑundefined. С ÑÑого моменÑа Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Ð¿ÐµÑеменнÑÑ. - ÐеÑеменной
phraseпÑиÑваиваеÑÑÑ Ð·Ð½Ð°Ñение. - ÐеÑеменнаÑ
phraseменÑÐµÑ Ð·Ð½Ð°Ñение.
Ðока ÑÑо вÑÑ Ð²ÑглÑÐ´Ð¸Ñ Ð¿ÑоÑÑо, пÑавда?
- ÐеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ â ÑÑо ÑвойÑÑво ÑпеÑиалÑного внÑÑÑеннего обÑекÑа, ÑвÑзанного Ñ ÑекÑÑим вÑполнÑÑÑимÑÑ Ð±Ð»Ð¾ÐºÐ¾Ð¼/ÑÑнкÑией/ÑкÑипÑом.
- РабоÑа Ñ Ð¿ÐµÑеменнÑми â ÑÑо на Ñамом деле ÑабоÑа Ñо ÑвойÑÑвами ÑÑого обÑекÑа.
«ÐекÑиÑеÑкое окÑÑжение» â ÑÑо обÑÐµÐºÑ ÑпеÑиÑикаÑии: он ÑÑÑеÑÑвÑÐµÑ ÑолÑко «ÑеоÑеÑиÑеÑки» в ÑпеÑиÑикаÑии ÑзÑка Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ñого, как вÑе ÑабоÑаеÑ. ÐÑ Ð½Ðµ можем полÑÑиÑÑ ÑÑÐ¾Ñ Ð¾Ð±ÑÐµÐºÑ Ð² наÑем коде и манипÑлиÑоваÑÑ Ð¸Ð¼ напÑÑмÑÑ.
JavaScript-движки Ñакже могÑÑ Ð¾Ð¿ÑимизиÑоваÑÑ ÐµÐ³Ð¾, оÑбÑаÑÑваÑÑ Ð½ÐµÐ¸ÑполÑзÑемÑе пеÑеменнÑе Ð´Ð»Ñ Ñкономии памÑÑи и вÑполнÑÑÑ Ð´ÑÑгие внÑÑÑенние дейÑÑвиÑ, но пÑи ÑÑом видимое поведение оÑÑаеÑÑÑ Ñаким, как опиÑано.
Шаг 2. Function Declaration
ФÑнкÑÐ¸Ñ â ÑÑо Ñоже знаÑение, как и пеÑеменнаÑ.
РазниÑа заклÑÑаеÑÑÑ Ð² Ñом, ÑÑо Function Declaration мгновенно иниÑиализиÑÑеÑÑÑ Ð¿Ð¾Ð»Ð½Ð¾ÑÑÑÑ.
Ðогда ÑоздаеÑÑÑ Ð»ÐµÐºÑиÑеÑкое окÑÑжение, Function Declaration ÑÑÐ°Ð·Ñ Ð¶Ðµ ÑÑановиÑÑÑ ÑÑнкÑией, гоÑовой к иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ (в оÑлиÑие Ð¾Ñ let, коÑоÑÑй до моменÑа обÑÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸ÑполÑзован).
Ðменно поÑÑÐ¾Ð¼Ñ Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вÑзваÑÑ ÑÑнкÑиÑ, обÑÑвленнÑÑ ÐºÐ°Ðº Function Declaration, до Ñамого ÐµÑ Ð¾Ð±ÑÑвлениÑ.
ÐоÑ, к пÑимеÑÑ, наÑалÑное ÑоÑÑоÑние глобалÑного лекÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ Ð¿Ñи добавлении ÑÑнкÑии:
ÐонеÑно, Ñакое поведение каÑаеÑÑÑ ÑолÑко Function Declaration, а не Function Expression, в коÑоÑÑÑ
Ð¼Ñ Ð¿ÑиÑваиваем ÑÑнкÑÐ¸Ñ Ð¿ÐµÑеменной, напÑимеÑ, let say = function(name) {...}.
Шаг 3. ÐнÑÑÑеннее и внеÑнее лекÑиÑеÑкое окÑÑжение
Ðогда запÑÑкаеÑÑÑ ÑÑнкÑиÑ, в наÑале ее вÑзова авÑомаÑиÑеÑки ÑоздаеÑÑÑ Ð½Ð¾Ð²Ð¾Ðµ лекÑиÑеÑкое окÑÑжение Ð´Ð»Ñ Ñ ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð»Ð¾ÐºÐ°Ð»ÑнÑÑ Ð¿ÐµÑеменнÑÑ Ð¸ паÑамеÑÑов вÑзова.
ÐапÑимеÑ, Ð´Ð»Ñ say("John") ÑÑо вÑглÑÐ´Ð¸Ñ Ñак (вÑполнение наÑ
одиÑÑÑ Ð½Ð° ÑÑÑоке, оÑмеÑенной ÑÑÑелкой):
РпÑоÑеÑÑе вÑзова ÑÑнкÑии Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð´Ð²Ð° лекÑиÑеÑÐºÐ¸Ñ Ð¾ÐºÑÑжениÑ: внÑÑÑеннее (Ð´Ð»Ñ Ð²ÑзÑваемой ÑÑнкÑии) и внеÑнее (глобалÑное):
-
ÐнÑÑÑеннее лекÑиÑеÑкое окÑÑжение ÑооÑвеÑÑÑвÑÐµÑ ÑекÑÑÐµÐ¼Ñ Ð²ÑполнениÑ
say.РнÑм Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑ Ð¾Ð´Ð½Ð° пеÑеменнаÑ
name, паÑамеÑÑ ÑÑнкÑии. ÐÑ Ð²ÑзÑваемsay("John"), Ñак ÑÑо знаÑение пеÑеменнойnameÑавно"John". -
ÐнеÑнее лекÑиÑеÑкое окÑÑжение â ÑÑо глобалÑное лекÑиÑеÑкое окÑÑжение.
РнÑм Ð½Ð°Ñ Ð¾Ð´ÑÑÑÑ Ð¿ÐµÑеменнаÑ
phraseи Ñама ÑÑнкÑиÑ.
У внÑÑÑеннего лекÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ ÐµÑÑÑ ÑÑÑлка на внеÑнее outer.
Ðогда код Ñ Ð¾ÑÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к пеÑеменной â ÑнаÑала пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ð¿Ð¾Ð¸Ñк во внÑÑÑеннем лекÑиÑеÑком окÑÑжении, заÑем во внеÑнем, заÑем в ÑледÑÑÑем и Ñак далее, до глобалÑного.
ÐÑли пеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð½Ðµ бÑла найдена, ÑÑо бÑÐ´ÐµÑ Ð¾Ñибкой в ÑÑÑогом Ñежиме (use strict). Ðез ÑÑÑогого Ñежима, Ð´Ð»Ñ Ð¾Ð±ÑаÑной ÑовмеÑÑимоÑÑи, пÑиÑваивание неÑÑÑеÑÑвÑÑÑей пеÑеменной ÑоздаÑÑ Ð½Ð¾Ð²ÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑнÑÑ Ð¿ÐµÑеменнÑÑ Ñ Ñаким же именем.
ÐавайÑе поÑмоÑÑим, как пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ð¿Ð¾Ð¸Ñк в наÑем пÑимеÑе:
- ÐÐ»Ñ Ð¿ÐµÑеменной
name,alertвнÑÑÑиsayÑÑÐ°Ð·Ñ Ð¶Ðµ Ð½Ð°Ñ Ð¾Ð´Ð¸Ñ ÐµÐµ во внÑÑÑеннем лекÑиÑеÑком окÑÑжении. - Ðогда
alertÑ Ð¾ÑÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп кphrase, он не Ð½Ð°Ñ Ð¾Ð´Ð¸Ñ ÐµÑ Ð»Ð¾ÐºÐ°Ð»Ñно, поÑÑÐ¾Ð¼Ñ Ð²ÑнÑжден обÑаÑиÑÑÑÑ Ðº внеÑÐ½ÐµÐ¼Ñ Ð»ÐµÐºÑиÑеÑÐºÐ¾Ð¼Ñ Ð¾ÐºÑÑÐ¶ÐµÐ½Ð¸Ñ Ð¸ Ð½Ð°Ñ Ð¾Ð´Ð¸ÑphraseÑам.
Шаг 4. ÐозвÑÐ°Ñ ÑÑнкÑии
ÐавайÑе веÑнÑмÑÑ Ðº пÑимеÑÑ Ñ makeCounter:
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
РнаÑале каждого вÑзова makeCounter() ÑоздаеÑÑÑ Ð½Ð¾Ð²Ñй обÑÐµÐºÑ Ð»ÐµÐºÑиÑеÑкого окÑÑжениÑ, в коÑоÑом Ñ
ÑанÑÑÑÑ Ð¿ÐµÑеменнÑе Ð´Ð»Ñ ÐºÐ¾Ð½ÐºÑеÑного запÑÑка makeCounter.
Таким обÑазом, Ð¼Ñ Ð¸Ð¼ÐµÐµÐ¼ два вложеннÑÑ Ð»ÐµÐºÑиÑеÑÐºÐ¸Ñ Ð¾ÐºÑÑжениÑ, как в пÑимеÑе вÑÑе:
ÐÑлиÑие заклÑÑаеÑÑÑ Ð² Ñом, ÑÑо во вÑÐµÐ¼Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ makeCounter() ÑоздаеÑÑÑ ÐºÑоÑеÑÐ½Ð°Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÑÑнкÑиÑ, ÑоÑÑоÑÑÐ°Ñ Ð²Ñего из одной ÑÑÑоки: return count++. ÐÑ ÐµÐµ еÑе не запÑÑкаем, а ÑолÑко Ñоздаем.
ÐÑе ÑÑнкÑии помнÑÑ Ð»ÐµÐºÑиÑеÑкое окÑÑжение, в коÑоÑом они бÑли ÑозданÑ. ТеÑ
ниÑеÑки здеÑÑ Ð½ÐµÑ Ð½Ð¸ÐºÐ°ÐºÐ¾Ð¹ магии: вÑе ÑÑнкÑии имеÑÑ ÑкÑÑÑое ÑвойÑÑво [[Environment]], коÑоÑое Ñ
ÑÐ°Ð½Ð¸Ñ ÑÑÑÐ»ÐºÑ Ð½Ð° лекÑиÑеÑкое окÑÑжение, в коÑоÑом бÑла Ñоздана ÑÑнкÑиÑ:
Таким обÑазом, counter.[[Environment]] Ð¸Ð¼ÐµÐµÑ ÑÑÑÐ»ÐºÑ Ð½Ð° {count: 0} лекÑиÑеÑкого окÑÑжениÑ. Так ÑÑнкÑÐ¸Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÐµÑ, где она бÑла Ñоздана, незавиÑимо Ð¾Ñ Ñого, где она вÑзÑваеÑÑÑ. СÑÑлка на [[Environment]] ÑÑÑанавливаеÑÑÑ Ð¾Ð´Ð¸Ð½ Ñаз и навÑегда пÑи Ñоздании ÑÑнкÑии.
ÐпоÑледÑÑвии, пÑи вÑзове counter(), Ð´Ð»Ñ ÑÑого вÑзова ÑоздаеÑÑÑ Ð½Ð¾Ð²Ð¾Ðµ лекÑиÑеÑкое окÑÑжение, а его внеÑнÑÑ ÑÑÑлка на лекÑиÑеÑкое окÑÑжение беÑеÑÑÑ Ð¸Ð· counter.[[Environment]]:
ТепеÑÑ, когда код внÑÑÑи counter() иÑÐµÑ Ð¿ÐµÑеменнÑÑ count, он ÑнаÑала иÑÐµÑ ÐµÐµ в ÑобÑÑвенном лекÑиÑеÑком окÑÑжении (пÑÑÑом, Ñак как Ñам Ð½ÐµÑ Ð»Ð¾ÐºÐ°Ð»ÑнÑÑ
пеÑеменнÑÑ
), а заÑем в лекÑиÑеÑком окÑÑжении внеÑнего вÑзова makeCounter(), где наÑ
Ð¾Ð´Ð¸Ñ count и изменÑÐµÑ ÐµÐµ.
ÐеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÑеÑÑÑ Ð² Ñом лекÑиÑеÑком окÑÑжении, в коÑоÑом она ÑÑÑеÑÑвÑеÑ.
ÐÐ¾Ñ ÑоÑÑоÑние поÑле вÑполнениÑ:
ÐÑли Ð¼Ñ Ð²Ñзовем counter() неÑколÑко Ñаз, Ñо в одном и Ñом же меÑÑе пеÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ count бÑÐ´ÐµÑ ÑвелиÑена до 2, 3 и Ñ.д.
РпÑогÑаммиÑовании еÑÑÑ Ð¾Ð±Ñий ÑеÑмин: «замÑкание», â коÑоÑÑй должен знаÑÑ ÐºÐ°Ð¶Ð´Ñй ÑазÑабоÑÑик.
ÐамÑкание â ÑÑо ÑÑнкÑиÑ, коÑоÑÐ°Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÐµÑ Ñвои внеÑние пеÑеменнÑе и Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ðº ним доÑÑÑп. РнекоÑоÑÑÑ ÑзÑÐºÐ°Ñ ÑÑо невозможно, или ÑÑнкÑÐ¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° бÑÑÑ Ð½Ð°Ð¿Ð¸Ñана ÑпеÑиалÑнÑм обÑазом, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑилоÑÑ Ð·Ð°Ð¼Ñкание. Ðо, как бÑло опиÑано вÑÑе, в JavaScript, вÑе ÑÑнкÑии изнаÑалÑно ÑвлÑÑÑÑÑ Ð·Ð°Ð¼ÑканиÑми (еÑÑÑ ÑолÑко одно иÑклÑÑение, пÑо коÑоÑое бÑÐ´ÐµÑ ÑаÑÑказано в СинÑакÑÐ¸Ñ "new Function").
То еÑÑÑ Ð¾Ð½Ð¸ авÑомаÑиÑеÑки запоминаÑÑ, где бÑли ÑозданÑ, Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÑкÑÑÑого ÑвойÑÑва [[Environment]], и вÑе они могÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к внеÑним пеÑеменнÑм.
Ðогда на ÑобеÑедовании ÑÑонÑенд-ÑазÑабоÑÑÐ¸ÐºÑ Ð·Ð°Ð´Ð°ÑÑ Ð²Ð¾Ð¿ÑоÑ: «ÑÑо Ñакое замÑкание?», â пÑавилÑнÑм оÑвеÑом бÑÐ´ÐµÑ Ð¾Ð¿Ñеделение замÑÐºÐ°Ð½Ð¸Ñ Ð¸ обÑÑÑÐ½ÐµÐ½Ð¸Ñ Ñого ÑакÑа, ÑÑо вÑе ÑÑнкÑии в JavaScript ÑвлÑÑÑÑÑ Ð·Ð°Ð¼ÑканиÑми, и, Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ, неÑколÑко Ñлов о ÑеÑ
ниÑеÑкиÑ
деÑалÑÑ
: ÑвойÑÑве [[Environment]] и о Ñом, как ÑабоÑÐ°ÐµÑ Ð»ÐµÐºÑиÑеÑкое окÑÑжение.
СбоÑка мÑÑоÑа
ÐбÑÑно лекÑиÑеÑкое окÑÑжение ÑдалÑеÑÑÑ Ð¸Ð· памÑÑи вмеÑÑе Ñо вÑеми пеÑеменнÑми поÑле завеÑÑÐµÐ½Ð¸Ñ Ð²Ñзова ÑÑнкÑии. ÐÑо ÑвÑзано Ñ Ñем, ÑÑо на него Ð½ÐµÑ ÑÑÑлок. Ðак и лÑбой обÑÐµÐºÑ JavaScript, оно Ñ ÑаниÑÑÑ Ð² памÑÑи ÑолÑко до ÑÐµÑ Ð¿Ð¾Ñ, пока к Ð½ÐµÐ¼Ñ Ð¼Ð¾Ð¶Ð½Ð¾ обÑаÑиÑÑÑÑ.
Ðднако еÑли ÑÑÑеÑÑвÑÐµÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÑÑнкÑиÑ, коÑоÑÐ°Ñ Ð²Ñе еÑе доÑÑÑпна поÑле завеÑÑÐµÐ½Ð¸Ñ ÑÑнкÑии, Ñо она Ð¸Ð¼ÐµÐµÑ ÑвойÑÑво [[Environment]], ÑÑÑлаÑÑееÑÑ Ð½Ð° лекÑиÑеÑкое окÑÑжение.
Ð ÑÑом ÑлÑÑае лекÑиÑеÑкое окÑÑжение оÑÑаеÑÑÑ Ð´Ð¾ÑÑÑпнÑм даже поÑле завеÑÑÐµÐ½Ð¸Ñ ÑабоÑÑ ÑÑнкÑии.
ÐапÑимеÑ:
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // g.[[Environment]] Ñ
ÑÐ°Ð½Ð¸Ñ ÑÑÑÐ»ÐºÑ Ð½Ð° лекÑиÑеÑкое окÑÑжение
// из ÑооÑвеÑÑÑвÑÑÑего вÑзова f()
ÐбÑаÑиÑе внимание, ÑÑо еÑли f() вÑзÑваеÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ Ñаз и ÑезÑлÑÑиÑÑÑÑие ÑÑнкÑии ÑоÑ
ÑанÑÑÑÑÑ, Ñо вÑе ÑооÑвеÑÑÑвÑÑÑие обÑекÑÑ Ð»ÐµÐºÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ Ñакже бÑдÑÑ ÑоÑ
ÑÐ°Ð½ÐµÐ½Ñ Ð² памÑÑи. РпÑиведенном ниже коде â вÑе ÑÑи:
function f() {
let value = Math.random();
return function() { alert(value); };
}
// 3 ÑÑнкÑии в маÑÑиве, ÐºÐ°Ð¶Ð´Ð°Ñ Ð¸Ð· коÑоÑÑÑ
ÑÑÑлаеÑÑÑ Ð½Ð° лекÑиÑеÑкое окÑÑжение
// из ÑооÑвеÑÑÑвÑÑÑего вÑзова f()
let arr = [f(), f(), f()];
ÐбÑÐµÐºÑ Ð»ÐµÐºÑиÑеÑкого окÑÑÐ¶ÐµÐ½Ð¸Ñ Ð¸ÑÑезаеÑ, когда ÑÑановиÑÑÑ Ð½ÐµÐ´Ð¾ÑÑÑпнÑм (как и лÑбой дÑÑгой обÑекÑ). ÐÑÑгими Ñловами, он ÑÑÑеÑÑвÑÐµÑ ÑолÑко до ÑÐµÑ Ð¿Ð¾Ñ, пока на него ÑÑÑлаеÑÑÑ Ñ Ð¾ÑÑ Ð±Ñ Ð¾Ð´Ð½Ð° Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÑÑнкÑиÑ.
РпÑиведенном ниже коде поÑле ÑÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾Ð¹ ÑÑнкÑии ее окÑÑжаÑÑее лекÑиÑеÑкое окÑÑжение (а знаÑиÑ, и value) оÑиÑаеÑÑÑ Ð¸Ð· памÑÑи:
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // пока ÑÑÑеÑÑвÑÐµÑ ÑÑнкÑÐ¸Ñ g, value оÑÑаеÑÑÑ Ð² памÑÑи
g = null; // ...и ÑепеÑÑ Ð¿Ð°Ð¼ÑÑÑ Ð¾ÑиÑена.
ÐпÑимизаÑÐ¸Ñ Ð½Ð° пÑакÑике
Ðак Ð¼Ñ Ð²Ð¸Ð´ÐµÐ»Ð¸, в ÑеоÑии, пока ÑÑнкÑÐ¸Ñ Ð¶Ð¸Ð²Ð°, вÑе внеÑние пеÑеменнÑе Ñоже ÑÐ¾Ñ ÑанÑÑÑÑÑ.
Ðо на пÑакÑике движки JavaScript пÑÑаÑÑÑÑ ÑÑо опÑимизиÑоваÑÑ. Ðни анализиÑÑÑÑ Ð¸ÑполÑзование пеÑеменнÑÑ Ð¸, еÑли легко по ÐºÐ¾Ð´Ñ Ð¿Ð¾Ð½ÑÑÑ, ÑÑо внеÑнÑÑ Ð¿ÐµÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð½Ðµ иÑполÑзÑеÑÑÑ â она ÑдалÑеÑÑÑ.
Ðдним из важнÑÑ Ð¿Ð¾Ð±Ð¾ÑнÑÑ ÑÑÑекÑов в V8 (Chrome, Edge, Opera) ÑвлÑеÑÑÑ Ñо, ÑÑо ÑÐ°ÐºÐ°Ñ Ð¿ÐµÑÐµÐ¼ÐµÐ½Ð½Ð°Ñ ÑÑановиÑÑÑ Ð½ÐµÐ´Ð¾ÑÑÑпной пÑи оÑладке.
ÐопÑобÑйÑе запÑÑÑиÑÑ ÑледÑÑÑий пÑÐ¸Ð¼ÐµÑ Ð² Chrome Ñ Ð¾ÑкÑÑÑой Developer Tools.
Ðогда код бÑÐ´ÐµÑ Ð¿Ð¾ÑÑавлен на паÑзÑ, напиÑиÑе в конÑоли alert(value).
function f() {
let value = Math.random();
function g() {
debugger; // в конÑоли: напиÑиÑе alert(value); Такой пеÑеменной неÑ!
}
return g;
}
let g = f();
g();
Ðак Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе видеÑÑ â Ñакой пеÑеменной не ÑÑÑеÑÑвÑеÑ! Ð ÑеоÑии, она должна бÑÑÑ Ð´Ð¾ÑÑÑпна, но попала под опÑимизаÑÐ¸Ñ Ð´Ð²Ð¸Ð¶ÐºÐ°.
ÐÑо Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑиводиÑÑ Ðº забавнÑм (еÑли ÑдаÑÑÑÑ ÑеÑиÑÑ Ð±ÑÑÑÑо) пÑоблемам пÑи оÑладке. Ðдна из Ð½Ð¸Ñ â Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑвидеÑÑ Ð½Ðµ ÑÑ Ð²Ð½ÐµÑнÑÑ Ð¿ÐµÑеменнÑÑ Ð¿Ñи ÑовпадаÑÑÐ¸Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÑÑ :
let value = "СÑÑпÑиз!";
function f() {
let value = "ближайÑее знаÑение";
function g() {
debugger; // в конÑоли: напиÑиÑе alert(value); СÑÑпÑиз!
}
return g;
}
let g = f();
g();
ÐÑÑ Ð¾ÑобенноÑÑÑ V8 полезно знаÑÑ. ÐÑли Ð²Ñ Ð·Ð°Ð½Ð¸Ð¼Ð°ÐµÑеÑÑ Ð¾Ñладкой в Chrome/Edge/Opera, Ñано или поздно Ð²Ñ Ñ Ð½ÐµÐ¹ ÑÑолкнÑÑеÑÑ.
ÐÑо не баг в оÑладÑике, а ÑкоÑее оÑобенноÑÑÑ V8. Ðозможно Ñо вÑеменем ÑÑо измениÑÑÑ. ÐÑ Ð²Ñегда можеÑе пÑовеÑиÑÑ ÑÑо, запÑÑÑив пÑимеÑÑ Ð½Ð° ÑÑой ÑÑÑаниÑе.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)