Repozytorium Web Developera

JavaScript - zasięg, hoisting, zmienne

Przydatne linki

Przechowywanie zmiennych

LocalStorage - pamięć podręczna/lokalna

LocalStorage przechowuje dane przez wszystkie sesje.

Zapis:

localStorage['myKey'] = 'somestring'; // only strings

Odczyt:

var myVar = localStorage['myKey'] || 'defaultValue';

Przechowywanie wartości złożonych można uzyskać poprzez zastosowanie JSON.

Zapis:

localStorage['myKey'] = JSON.stringify(myVar);

Odczyt:

var stored = localStorage['myKey'];
if (stored) myVar = JSON.parse(stored);
else myVar = {a:'test', b: [1, 2, 3]};

Domknięcia i zasięg zmiennych

Domknięcia oznaczają to, że zasięg zawsze posiada dostęp do zewnętrznego zasięgu, w którym został zdefiniowany.

Ponieważ zasięg w JavaScript można definiować tylko poprzez funkcję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

Zasięg zmiennych w pętlach - przykład 1

W JavaScript pętle nie tworzą zasięgu. Dla poniższego kodu najlepiej obrazuje to drugi console.log, gdzie zmienna x przechowywana jest poza pętlą for, ale nie znamy już jej wartości poza funkcją example.

var x = "x spoza funkcji";
function example()
{
    for (var i = 0; i < 1; i++)
    {
        var x = "x z pętli for";
        console.log(x);             // x z petli for
    }
    console.log(x);                 // x z petli for
}
example();
console.log(x);                     // x spoza funkcji

Zasięg zmiennych w pętlach - przykład 2

Poniższy przykład wypisze dziesięc razy liczbę 10 w konsoli (a nie od 0 do 9), ponieważ kod wewnątrz pętli jest opóźniony i wyświetla 10 razy ostatnią wartość i.

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

Konieczne jest przekopiowanie wartości i jak na poniższym przykładzie.

//wersja 1
for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);
        }, 1000);
    })(i);
}

//wersja 2
for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

Node.js - przykład 3

Pętla 2 implementuje funkcję opakowującą, która zapewnia domknięcie funkcji asynchronicznej logCar(). Kod w linii 8-13 implementuje podstawowe wywołanie zwrotne, co przekłada się na wyświetlanie jedynie ostatniej wartości z pętli w każdej iteracji. Kod 15-22 jest już prawidłowy.

function logCar(logMsg, callback){
  process.nextTick(function() {
    callback(logMsg);
  });
}
var cars = ["Ferrari", "Porsche", "Bugatti"];
// pętla 1
for (var idx in cars){
  var message = "Widziano samochód " + cars[idx];
  logCar(message, function(){
    console.log("Normalne wywołanie zwrotne: " + message);
  });
}
// pętla 2
for (var idx in cars){
  var message = "Widziano samochód " + cars[idx];
  (function(msg){
    logCar(msg, function(){
      console.log("Wywołanie zwrotne z domknięciem: " + msg);
    });
  })(message);
}

Na podstawie podrozdziału Implementowanie domknięcia w wywołaniach zwrotnych strona 89 z 4 rozdziału książki Node.js, MongoDB, AngularJS - Kompendium wiedzy. Brad Dayley

Hoisting i zasięg zmiennej

Dość istotny jest następujący przykład. Jeśli w danym zasięgu zakrywamy jakąś zmienną, a chcemy poznać jej wartość przed jej deklaracją w danym zasięgu, to hoisting sprawi, że wartość z poziomu wyżej zostanie przykryta wartością undefined, dlatego też w poniższym przykładzie pierwszy console.log zwraca taką wartość.

var x = 10;
function example2()
{
    //var x - tutaj tak naprawdę się znajduje
    console.log(x);                 // undefined
    var x = 2;
    console.log(x);                 // 2
}
example2();
console.log(x);

Kontekst this

function foo() {
  console.log(this);
}

foo(); // logs out the global e.g. `window` in browsers
let bar = {
  foo
}
bar.foo(); // Logs out `bar` as `foo` was called on `bar`
Źródło