Skip to content

Latest commit

 

History

History
230 lines (171 loc) · 11.1 KB

File metadata and controls

230 lines (171 loc) · 11.1 KB

Зоны бачнасці і прасторы імёнаў

Негледзячы на тое, што JavaScript добра працуе з сінтаксісам фігурных дужак для блокаў, у ім няма падтрымкі блочнай зоны бачнасці; усё што ёсць на гэты конт у мове - зона бачнасці функцыі.

function test() { // зона бачнасці
    for(var i = 0; i < 10; i++) { // не зона бачнасці
        // лічым
    }
    console.log(i); // 10
}

Заўвага: Калі не выкарыстана ў прысвойванні, аператары return або аргуменце функцыі, натацыя {...} будзе інтэрпрэтавана як блочны выраз, а не як літэрал аб'екта. Гэта з'ява, сумесна з аўтаматычнай устаўкай коскі з кропкай, можа прывесці да хітрых памылак.

Таксама JavaScript не падтрымлівае выразныя прасторы імёнаў, усё аб'яўляецца ў агульнадаступнай прасторы імёнаў.

Для кожнай спасылкі на пераменную, JavaScript пойдзе ўверх па ўсіх зонах бачнасці, пакуль не знойдзе яе. У выпадку, калі ён дойдзе да глабальнай прасторы імён і ўсё яшчэ не знойдзе неабходнае імя, ён уздыме ReferenceError.

Атрута глабальнымі пераменнымі

// скрыпт A
foo = '42';

// скрыпт B
var foo = '42'

Вышэйпрыведзеныя скрыпты маюць розныя вынікі. Скрыпт A аб'яўляе пераменную foo у глабальнай зоне бачнасці, скрыпт B аб'яўляе foo у актуальнай зоне бачнасці.

Паўторымся, гэта абсалютна не той жа самы вынік: не выкарыстоўваенне var можа мець сур'ёзныя наступствы.

// глабальная зона бачнасці
var foo = 42;
function test() {
    // лакальная зона бачнасці
    foo = 21;
}
test();
foo; // 21

З-за таго, што аператар var прапушчаны ўнутры функцыі test, значэнне foo у глабальнай прасторы імён будзе перазапісаным. Хаця першапачаткова гэта можа падацца невялікай праблемай, не выкарыстоўванне var у кодзе на тысячы радкоў, прывядзе да жахлівых, цяжкіх для адладкі памылак.

// глабальная прастора імёнаў
for(var i = 0; i < 10; i++) {
    subLoop();
}

function subLoop() {
    // прастора імёнаў subLoop
    for(i = 0; i < 10; i++) { // аператар var прапушчаны
        // робім чароўныя рэчы!
    }
}

Знешні цыкл скончыцца пасля першага выкліка subLoop, бо subLoop перазапісвае глабальную пераменную i. Выкарыстоўваючы var для другога цыкла for можна было б пазбегнуць памылкі. Ніколі не прапускайце аператар var, акрамя выпадкаў, калі змена дадзеных у знешняй зоне бачнасці ёсць пажаданым вынікам.

Лакальныя пераменныя

Адзіная крыніца лакальных пераменных у JavaScript гэта параметры функцыі і пераменныя аб'яўленыя праз аператар var.

// глабальная зона бачнасці
var foo = 1;
var bar = 2;
var i = 2;

function test(i) {
    // лакальная зона бачнасці функцыі test
    i = 5;

    var foo = 3;
    bar = 4;
}
test(10);

foo і i гэта лакальныя пераменныя унутры зоны бачнасці функцыі test, а вось прызначэнне bar перазапіша глабальныю пераменную з тым жа іменем.

Падыманне

JavaScript падымае аб'яўленні. Гэта азначае, што абодва аб'яўленні аператараў var і function падымуцца на верх іх зоны бачнасці.

bar();
var bar = function() {};
var someValue = 42;

test();
function test(data) {
    if (false) {
        goo = 1;

    } else {
        var goo = 2;
    }
    for(var i = 0; i < 100; i++) {
        var e = data[i];
    }
}

Вышэйпрыведзены код трансфармуецца перад пачаткам выконвання. JavaScript падымае аператары var, як і аб'яўленне function, наверх бліжэйшай зоны бачнасці.

// аператар var перамяшчаецца сюды
var bar, someValue; // па змоўчванню - 'undefined'

// аб'яўленне функцыі таксама падымаецца наверх
function test(data) {
    var goo, i, e; // адсутная блочная зона бачнасці перайшла сюды
    if (false) {
        goo = 1;

    } else {
        goo = 2;
    }
    for(i = 0; i < 100; i++) {
        e = data[i];
    }
}

bar(); // падае з TypeError бо ўсё яшчэ 'undefined'
someValue = 42; // прысвойванні не падымаюцца
bar = function() {};

test();

Адсутнасць блочнай зоны бачнасці не толькі падыме аператар var па-за межы цыкла і яго цела, але таскама зробіць вынік некаторых канструкцый if не-інтуітыўным.

Хоць у арыгінальным кодзе падаецца што канструкцыя if змяняе глабальную пераменную goo, на дадзены момант гэта мяняе лакальную пераменную - пасля таго, як было прыменена падыманне.

Без ведаў аб падыманні, можна падумаць што код ніжэй кіне ReferenceError.

// правярае ці было SomeImportantThing праініцыалізавана
if (!SomeImportantThing) {
    var SomeImportantThing = {};
}

Але канешне, гэта працуе праз тое, што аператар var быў падняты на верх глабальнай зоны бачнасці.

var SomeImportantThing;

// тут нейкі код можа ініцыалізаваць SomeImportantThing, або не

// тут у гэтым можна ўпэўніцца
if (!SomeImportantThing) {
    SomeImportantThing = {};
}

Парадак доступу да пераменных

Усе зоны бачнасці ў JavaScript, уключаючы глабальную зону бачнасці, маюць адмысловае імя this, аб'яўленае ўнутры іх, якое спасылаецца на актуальны аб'ект.

Зоны бачнасці функцый таксама маюць імя arguments, аб'яўленае ў іх, якое спасылаецца на аргументы, што былі перададзеныя ў функцыю.

Напрыклад, калі паспрабаваць атрымаць доступ да пераменнай foo унутры зоны бачнасці функцыі, JavaScript будзе шукаць імя ў наступным парадку:

  1. У выпадку калі прысутнічае канструкцыя var foo у актуальнай зоне бачнасці, ёна і выкарыстоўваецца.
  2. Калі параметр функцыі мае імя foo, ён будзе выкарыстаны.
  3. Калі сама функцыя называецца foo, яна будзе выкарыстана.
  4. Пераходзіць у знешнюю зону бачнасці, і пачынае з пункта #1.

Заўвага: Наява параметра названага arguments перадухіліць стварэнне параметра arguments па змоўчванні.

Прасторы імёнаў

Вялікая праблема, звязаная з выкарыстоўваннем глабальнай прасторы імёнаў, гэта высокая верагоднасць перасячэння імёнаў пераменных. У JavaScript, гэта праблема можа быць лёгка пазбегнута праз выкарыстанне ананімных абгортак.

(function() {
    // аўтаномная "прастора імён"

    window.foo = function() {
        // адкрытае замыканне
    };

})(); // імгненнае выкананне функцыі

Ананімныя функцыі з'яўляюцца выразамі; таму каб быць выкліканымі, яны спачатку маюць быць ацэненымі.

( // ацэньваем функцыю ўнутры дужак
function() {}
) // вяртаем аб'ект функцыі
() // выклік выніку ацэнкі

Ёсць і іншыя спосабы ацаніць і імгненна выклікаць выраз функцыі, хаця яны і адрозніваюцца па сінтаксісу, паводзяць сябе аднолькава.

// Яшчэ некалькі спосабаў наўпрост выклікаць функцыю
!function(){}()
+function(){}()
(function(){}());
// і так далей...

Заключэнне

Рэкамендуецца заўсёды выкарыстоўваць ананімную абгортку каб інкапсуліраваць код у яго асабістай прасторы імёнаў. Гэта не толькі абараняе код ад перасячэння імёнаў, але і дапамагае падзяляць праграму на модулі.

Таксама выкарыстанне глабальных пераменных лічыцца дрэннай практыкай. Любое іх выкарыстоўванне - прыкмета дрэнна напісанага кода, схільнага да памылак, і цяжага ў падтрымцы.