Dərs 11 Orta səviyyə

Scoping və dəyişənlərin həyat dövrü

JavaScript-də scope anlayışını, dəyişənlərin görünürlüyünü, hoisting mexanizmini və dəyişənlərin həyat dövrünü ətraflı öyrənin.

Bu dərsdə öyrənəcəksiniz

  • Scope anlayışı və scope növləri (block, function, global)
  • Lexical scoping və scope chain mexanizmi
  • Variable shadowing və onun təsirləri
  • Hoisting mexanizmi və Temporal Dead Zone (TDZ)
  • Dəyişənlərin həyat dövrü (creation, initialization, assignment)

01 Scope nədir?

Scope proqramın hansı hissələrində hansı dəyişənlərə müraciət edə biləcəyinizi müəyyən edir. Bu, JavaScript-də kodun təşkilinin əsas konsepsiyasıdır.

Tərif:Scope dəyişənlərin, funksiyaların və obyektlərin əlçatan olduğu kodun sahəsidir. Scope hansı dəyişənlərin görünən və istifadə edilə bilən olduğunu müəyyən edir.

Sadə şəkildə desək, scope sizə deyir ki, kodunuzun müəyyən bir nöqtəsində hansı dəyişənlərə "çata" bilərsiniz.

javascript
// Scope nümunəsi
let globalVar = "I'm global";

function myFunction() {
  let functionVar = "I'm local to function";
  console.log(globalVar);      // Əlçatandır ✓
  console.log(functionVar);    // Əlçatandır ✓
}

myFunction();
console.log(globalVar);        // Əlçatandır ✓
console.log(functionVar);      // ReferenceError: functionVar is not defined
Vacib:Hər bir funksiyanın və blokun öz scope-u var. İçəridəki scope xaricdəki scope-dakı dəyişənlərə çata bilər, amma əksi mümkün deyil.

02 Block scope

letconst ilə yaradılan dəyişənlər block scope-a malikdir. Bu o deməkdir ki, onlar yalnız yaradıldıqları blok daxilində mövcuddur.

Blok nədir?

Blok mötərizələr {} ilə əhatə olunmuş kod parçasıdır. Bloklar if, for, while və digər strukturlarda istifadə olunur.

javascript
// Blok nümunələri
{
  // Bu blok daxilindədir
}

if (true) {
  // if bloku
}

for (let i = 0; i < 5; i++) {
  // for bloku
}

let və const ilə block scope

let və const ilə yaradılan dəyişənlər yalnız yaradıldıqları blok daxilində əlçatandır:

javascript
{
  let blockLet = "I'm in a block";
  const blockConst = "Me too!";
  console.log(blockLet);    // I'm in a block
  console.log(blockConst);  // Me too!
}

console.log(blockLet);      // ReferenceError: blockLet is not defined
console.log(blockConst);    // ReferenceError: blockConst is not defined
Əsas Qayda:Block scope daxilində yaradılan let və const dəyişənləri blokdan kənarda mövcud olmur və istifadə edilə bilməz.

Praktik nümunələr

Gəlin block scope-un real istifadə hallarına baxaq:

javascript
// if blokunda
if (true) {
  let message = "Hello from if block";
  console.log(message);  // Hello from if block
}
// console.log(message); // ReferenceError

// for loopunda
for (let i = 0; i < 3; i++) {
  let loopVar = i * 2;
  console.log(loopVar);  // 0, 2, 4
}
// console.log(i);        // ReferenceError
// console.log(loopVar);  // ReferenceError

03 Function scope

var ilə yaradılan dəyişənlər function scope-a malikdir. Bu o deməkdir ki, onlar bütün funksiyanın daxilində əlçatandır, lakin blok sərhədlərinə riayət etmirlər.

var-ın davranışı

var ilə yaradılan dəyişənlər funksiyanın hər yerindən əlçatandır, blok daxilində yaradılsalar belə:

javascript
function testVar() {
  var functionScoped = "I'm function scoped";

  if (true) {
    var alsoFunctionScoped = "Me too!";
    console.log(functionScoped);      // İşləyir ✓
  }

  console.log(alsoFunctionScoped);    // Hələ də işləyir ✓
}

testVar();
// console.log(functionScoped);       // ReferenceError

var və let/const müqayisəsi

Gəlin var və let/const arasındakı fərqə baxaq:

javascript
// var blokdan kənarda əlçatandır
if (true) {
  var varVariable = "I'm var";
  let letVariable = "I'm let";
}

console.log(varVariable);  // I'm var
console.log(letVariable);  // ReferenceError: letVariable is not defined

// Loop dəyişənləri
for (var i = 0; i < 3; i++) {
  // loop body
}
console.log(i);  // 3

for (let j = 0; j < 3; j++) {
  // loop body
}
// console.log(j);  // ReferenceError: letVariable is not defined
Tövsiyə:Modern JavaScript-də var-dan istifadə etməyin. Həmişə let və const istifadə edin, çünki onlar daha təhlükəsiz və gözləniləndir.

04 Global scope

Hər hansı funksiya və ya blokdan kənarda yaradılan dəyişənlər global scope-dadır. Global dəyişənlərə kodunuzun istənilən yerindən çatmaq olar.

Global dəyişənlər

Global scope-da yaradılan dəyişənlər bütün proqramınızda əlçatandır:

javascript
// Global dəyişənlər
let globalLet = "Global with let";
const globalConst = "Global with const";
var globalVar = "Global with var";

function accessGlobals() {
  console.log(globalLet);    // Əlçatandır
  console.log(globalConst);  // Əlçatandır
  console.log(globalVar);    // Əlçatandır
}

accessGlobals();

Global obyekt

Brauzerdə global scope window obyektinə, Node.js-də isə global obyektinə bağlıdır:

javascript
// Brauzerdə
var globalVar = "I'm on window";
let globalLet = "I'm NOT on window";

console.log(window.globalVar);  // I'm on window
console.log(window.globalLet);  // undefined

// Global funksiya
function sayHello() {
  console.log("Hello!");
}

window.sayHello();  // Hello!
Diqqət:Global dəyişənlərdən ehtiyatla istifadə edin. Çoxlu global dəyişən kodunuzu çətin başa düşülən edə bilər. Həmişə dəyişənlərin scope-unu mümkün qədər məhdudlaşdırın.

05 Lexical scoping

Lexical scoping (həmçinin static scoping kimi tanınır) JavaScript-in scope-ları necə müəyyən etdiyini izah edir. Dəyişənlərin scope-u kodun yazıldığı yerə görə müəyyən olunur.

Tərif:Lexical scoping o deməkdir ki, daxili funksiyalar xarici funksiyanın dəyişənlərinə çata bilər. Scope kodun strukturuna görə müəyyən olunur.

İç-içə funksiyalar

İçəridəki funksiyalar xaricdəki funksiyanın dəyişənlərinə çata bilər:

javascript
function outer() {
  let outerVar = "I'm from outer";

  function inner() {
    let innerVar = "I'm from inner";
    console.log(outerVar);  // Əlçatandır ✓
    console.log(innerVar);  // Əlçatandır ✓
  }

  inner();
  // console.log(innerVar);  // ReferenceError: innerVar is not defined
}

outer();

Scope chain

JavaScript dəyişən axtararkən scope chain-dən istifadə edir:

  • Cari scope-da dəyişən axtarılır
  • Tapılmazsa, bir səviyyə yuxarı (xarici scope) axtarılır
  • Bu proses dəyişən tapılana və ya global scope-a çatana qədər davam edir
  • Heç yerdə tapılmazsa, ReferenceError baş verir
javascript
let global = "Global scope";

function levelOne() {
  let one = "Level 1";

  function levelTwo() {
    let two = "Level 2";

    function levelThree() {
      let three = "Level 3";

      // Scope chain işləyir
      console.log(three);   // Səviyyə 3-dən
      console.log(two);     // Səviyyə 2-dən
      console.log(one);     // Səviyyə 1-dən
      console.log(global);  // Global scope-dan
    }

    levelThree();
  }

  levelTwo();
}

levelOne();

06 Variable shadowing

Variable shadowing daxili scope-da xarici scope-dakı dəyişənlə eyni adda dəyişən yaradarkən baş verir. Daxili dəyişən xarici dəyişəni "kölgələyir".

Tərif:Shadowing daxili scope-da xarici scope-dakı dəyişənlə eyni adda yeni dəyişən yaratmaq deməkdir. Daxili scope-da daxili dəyişən istifadə olunur, xarici dəyişən isə "gizlənir".

Shadowing nümunəsi

Eyni adlı müxtəlif scope-larda dəyişənlər yarada bilərik:

javascript
let name = "Global";

function greet() {
  let name = "Function";  // Global name-i kölgələyir
  console.log(name);      // Function

  if (true) {
    let name = "Block";   // Function name-i kölgələyir
    console.log(name);    // Block
  }

  console.log(name);      // Function
}

greet();
console.log(name);        // Global

Shadowing nə zaman baş verir?

  • Daxili funksiyada xarici funksiyanın dəyişəni ilə eyni adda dəyişən yaradarkən
  • Blokda xarici scope-dakı dəyişənlə eyni adda dəyişən yaradarkən
  • Funksiya parametri xarici scope-dakı dəyişənlə eyni adda olanda
Məsləhət:Shadowing-dən mümkün qədər qaçın. Çaşqınlığa səbəb ola bilər. Fərqli adlar istifadə edin və kodunuzu daha aydın edin.

07 Hoisting

Hoisting JavaScript-in dəyişən və funksiya bəyannamələrini scope-un yuxarısına "qaldırması" prosesidir. Bu, kodun icrasından əvvəl baş verir.

Tərif:Hoisting JavaScript-in dəyişən və funksiya bəyannamələrini scope-un başına köçürməsi mexanizmidir. Lakin təyin etmələr (assignments) köçürülmür, yalnız bəyannamələr.

var hoisting

var ilə yaradılan dəyişənlər hoisted olunur və undefined ilə başlanğıc dəyər alır:

javascript
console.log(x);  // undefined
var x = 5;
console.log(x);  // 5

// Yuxarıdakı kod belə işləyir:
// var x;
// console.log(x);  // undefined
// x = 5;
// console.log(x);  // 5

Funksiya hoisting

Funksiya bəyannamələri (function declarations) tam şəkildə hoisted olunur:

javascript
// Funksiya bəyannaməsi hoisted olunur
greet();  // Hello!

function greet() {
  console.log("Hello!");
}

// Function expression hoisted olunmur
// sayHi();  // ReferenceError

const sayHi = function() {
  console.log("Hi!");
};
Qeyd:Yalnız function declaration hoisted olunur. Function expression (const func = function() {}) hoisted olunmur.

let və const hoisting

letconst da texniki olaraq hoisted olunur, lakin onlara bəyannamədən əvvəl müraciət etmək mümkün deyil:

javascript
// TDZ xətası
console.log(x);  // ReferenceError: Cannot access before initialization
let x = 5;

console.log(y);  // ReferenceError: Cannot access before initialization
const y = 10;

08 Temporal dead zone (TDZ)

Temporal dead zone (TDZ) scope-un başlanğıcından dəyişənin bəyan edilməsinə qədər olan zaman aralığıdır. Bu müddətdə dəyişənə müraciət etmək xəta verir.

Tərif:TDZ let və const dəyişənlərinin scope-un başlanğıcından faktiki bəyannaməsinə qədər olan müddətdir. Bu müddətdə dəyişənə müraciət edilə bilməz.

TDZ nümunəsi

Gəlin TDZ-nin necə işlədiyinə baxaq:

javascript
// TDZ nümunəsi
{
  // TDZ başlanğıcı - hələ əlçatan deyil
  // console.log(x);  // ReferenceError: Cannot access before initialization
  // console.log(y);  // ReferenceError: Cannot access before initialization

  let x = 10;      // TDZ sonu - artıq əlçatandır
  const y = 20;    // TDZ sonu - artıq əlçatandır

  console.log(x);  // 10
  console.log(y);  // 20
}

TDZ niyə mövcuddur?

  • Dəyişənlərin bəyan edilməmişdən əvvəl istifadəsini önləyir
  • Kodda səhvləri daha tez aşkar etməyə kömək edir
  • Const dəyişənlərinin məntiqini qoruyur (həmişə dəyərlə yaradılmalı)
Ən yaxşı təcrübə:Həmişə dəyişənləri scope-un yuxarısında bəyan edin. Bu TDZ problemlərinin qarşısını alır və kodu daha oxunaqlı edir.

09 Dəyişənlərin həyat dövrü

Hər dəyişənin JavaScript-də həyat dövrü var: creation (yaradılma), initialization (başlanğıc dəyər), və assignment (dəyər təyin etmə). Bu fazalar var, let və const üçün fərqlidir.

Üç faza

1. Creation (yaradılma)

Dəyişən scope-da qeydə alınır, lakin hələ dəyər almayıb:

  • var: undefined ilə initialize olunur
  • let/const: initialize olunmur (TDZ başlayır)

2. Initialization (başlanğıc dəyər)

Dəyişənə ilk dəfə dəyər verilir:

  • var: avtomatik olaraq undefined alır
  • let/const: bəyannamə xəttində initialize olunur (TDZ bitir)

3. Assignment (dəyər təyin etmə)

Dəyişənə yeni dəyər verilir:

  • var və let: dəyərlər dəyişdirilə bilər
  • const: yalnız bir dəfə, initialization zamanı təyin edilə bilər

Həyat dövrü müqayisəsi

Gəlin var və let/const-ın həyat dövrünü müqayisə edək:

javascript
// var vs let/const həyat dövrü
console.log(varVariable);    // undefined (hoisted və initialized)
// console.log(letVariable); // ReferenceError (hoisted amma TDZ-də)

var varVariable = "var value";
let letVariable = "let value";

console.log(varVariable);    // var value
console.log(letVariable);    // let value

// var: creation → initialization (undefined) → assignment
// let: creation (TDZ) → initialization → assignment
// const: creation (TDZ) → initialization və assignment birlikdə
// const yalnız bəyannamə zamanı dəyər ala bilər

10 Ən yaxşı təcrübələr

Scope və dəyişənlərlə işləyərkən bu qaydalara əməl edin:

constlet istifadə edin

var-dan istifadə etməyin. const və let daha təhlükəsiz, gözlənilən və block scope-a malikdir. Const-u prioritet verin, yalnız dəyər dəyişməli olduqda let istifadə edin.

var-dan qaçın

var köhnə və problemlidir. Function scope və hoisting davranışı gözlənilməz xətalara səbəb ola bilər. Modern JavaScript kodunda var-a ehtiyac yoxdur.

Dəyişənləri scope-un əvvəlində bəyan edin

Dəyişənləri scope-un yuxarısında bəyan etmək kodu daha oxunaqlı edir və TDZ problemlərinin qarşısını alır.

Scope-u mümkün qədər məhdudlaşdırın

Dəyişənləri ehtiyac duyulduqları ən kiçik scope-da yaradın. Bu ad toqquşmalarının qarşısını alır və kodu daha anlaşıqlı edir.

Shadowing-dən qaçın

Xarici scope-dakı dəyişənlərlə eyni adda dəyişən yaratmayın. Fərqli, aydın adlar istifadə edin.

Xatırlayın:Scope-u başa düşmək JavaScript-də peşəkar kod yazmaq üçün əsasdır. Bu konsepsiyaları mənimsəmək sizə daha yaxşı, təmiz və xətasız kod yazmağa kömək edəcək.

Xülasə

Scope dəyişənlərin əlçatanlığını müəyyən edir: block, function və global scope növləri var
let və const block scope-a, var isə function scope-a malikdir
Lexical scoping və scope chain daxili scope-ların xarici scope-lara çata bilməsini təmin edir
Variable shadowing daxili scope-da xarici dəyişənlə eyni adda dəyişən yaratmaq deməkdir
Hoisting dəyişən və funksiya bəyannamələrini scope-un yuxarısına köçürür
Temporal Dead Zone (TDZ) let və const dəyişənlərinə bəyannamədən əvvəl müraciət edilməsinin qarşısını alır
Dəyişənlərin həyat dövrü creation, initialization və assignment fazalarından ibarətdir

Təklifiniz var, səhv və ya xəta tapdınız? Zəhmət olmasa bizimlə əlaqə saxlayın.