Lexical scope, function vs. block scope, hoisting, scope closures

28
Lexical Scope, Function vs. Block Scope, Hoisting, Scope Closures DevDuck |

Transcript of Lexical scope, function vs. block scope, hoisting, scope closures

Lexical Scope,Function vs. Block Scope, Hoisting, Scope Closures

DevDuck |

Maciej CąderekFullstack JS Developer @Brainhub

Czym jest scope?

Zestaw regułdefiniujących zakres widoczności

(dostępności) zmiennych.

Lexical scope

● Zasięg zmiennych jest określany na podstawie ich umiejscowienia w kodzie i kontekstu w jakim się znajdują

● Scope zmiennych jest stały, określany na etapie kompilacji (wczesne wiązanie)

● W JavaScripcie scope jest określany poprzez funkcje (ES5) oraz bloki kodu (ES6)

Lexical scope

● Kod wewnątrz scope'a ma dostęp do zmiennych zadeklarowanych w scopie zewnętrznym

● W pierwszej kolejności wykorzystywane są zmienne w lokalnym zasięgu, następnie kolejne poziomy zagnieżdzenia scope'a

const a = 1;const b = 2;

function x() { const b = 3; console.log(a); // => 1 console.log(b); // => 3}

Function scope

● Jedyny dostępny sposób określania zasięgu zmiennych w ES5

● Najmniejszą jednostką scope'a jest funkcja

● Mechanizm specyficzny dla JavaScriptu

● Do deklaracji zmiennej wewnątrz scope'a służy słowo kluczowe var

● Użycie var w ES6+ jest niezalecane

Czym jest hoisting?

● Dwie fazy przetwarzania kodu - kompilacja i wykonanie

var a = 1;

var myFunction = function () { // code...};

function myOtherFunction() { // code...}

myFunction();myOtherFunction();

Czym jest hoisting?● W fazie kompilacji wczytywane są

jedynie deklaracje (zarówno zmiennych jak i funkcji)

var a;

var myFunction;

function myOtherFunction() { // code...}

a = 1;

myFunction = function () { // code...};

myFunction();myOtherFunction();

● W fazie wykonywania kodu przetwarzane są pozostałe instrukcje - przypisania zmiennych, wywołania funkcji itp

Hoisting - minusy

● Hoisting deklaracji zmiennych może prowadzić do nieczytelnego, nieoczywistego kodu

var a = 1;

function doSomeStuff() { a = 2; var a = 3;

return a;}

console.log(doSomeStuff()); // => 3console.log(a); // => 1

Hoisting - plusy● Hoisting funkcji pozwala na czytelniejszy zapis kodu - funkcje zawierające główną

logikę można umieścić przed funkcjami pomocniczymi

function doSomeStuff() { runHelper(); // other code...}

function runHelper() { // code...}

var● Ze względu na hoisting wszystkie deklaracje powinny znajdować się na początku

funkcji

● Możliwa ponowna deklaracja zmiennej

function doSomeStuff() { // all declarations with optional assigments: var a; var b = 'something'; var c = 'something';

// rest of the code...}

Block scope

● Sposób określania zasięgu zmiennych dostępny od ES6

● Najmniejszą jednostką scope'a jest blok kodu reprezentowany przez nawiasy klamrowe

● Mechanizm znany z wiekszości języków programowania

● Do deklaracji zmiennej wewnątrz block scope'a służą słowa kluczowe let oraz const

let & const

● Brak możliwości ponownej deklaracji zmiennej

● Dodatkowo const nie pozwala na ponowne przypisanie wartości do zmiennej (nie mylić z modyfikacją typów złożonych)

● Deklaracje zmiennych powinny znajdować się jak najbliżej ich wykorzystania

Kiedy var, let a kiedy const?

● Zasada: zawsze preferuj niemutowalny kod

● Dlaczego? Kod jest łatwiejszy w zrozumieniu i debugowaniu

● W ES6+ var nie powinno być nigdy stosowane

● const powinien stanowić domyślny wybór

● let powinien być stosowany tylko wtedy, gdy wartość zmiennej jest ponownie przypisywana (np. licznik pętli)

Zalety block scope

● Brak hoistingu - mniejsza podatność na błędy

function changeStyle() { const header = document.getElementById('header'); const article = document.getElementById('article'); const footer = document.getElementById('footer'); const mainColor = '#FF0000'; const accentColor = '#928c00';

header.style.backgroundColor = mainColor; header.style.color = accentColor; footer.style.backgroundColor = accentColor;}

Zalety block scope

● Lepsza organzacja kodu - deklaracje zmiennych w miejscu ich użycia

function changeStyle() { const mainColor = '#FF0000';

const header = document.getElementById('header'); const accentColor = '#928c00';

header.style.backgroundColor = mainColor; header.style.color = accentColor;

const footer = document.getElementById('footer');

footer.style.backgroundColor = mainColor;}

Zalety block scope

● Ograniczenie zasięgu zmiennych do wymaganego minimum

function changeStyle() { const mainColor = '#FF0000'; { const header = document.getElementById('header'); const accentColor = '#928c00'; header.style.backgroundColor = mainColor; header.style.color = accentColor; }

{ const footer = document.getElementById('footer'); footer.style.backgroundColor = mainColor; }}

Symulowanie bock scope'a w ES5

● Do symulowania block scope'a służy IIFE ( immediately-invoked function expression )

function changeRestStyle() { var mainColor = '#FF0000';

(function () { var header = document.getElementById('header'); var accentColor = '#928c00'; header.style.backgroundColor = mainColor; header.style.color = accentColor; }());

(function () { var footer = document.getElementById('footer'); footer.style.backgroundColor = mainColor; })();}

First-class functions

● Funkcje w JavaScript są obiektami pierwszej kategorii - mogą być traktowane jak wartości i przypisywane do zmiennych oraz stanowić elementy tablic i pola obiektów,

● Funkcje mogą być przekazywane jako argumenty do innych funkcji, mogą także stanowić wartość zwracaną z funkcji

● Funkcje, które przyjmuja inną funkcje jako argument lub zwracają inną funkcję są nazywane funkcjami wyższego rzedu (higher order functions)

● Funkcje mogą byc dowolnie zagnieżdżane, każda funkcja tworzy nowy scope

Czym jest domknięcie (closure)?

● Domknięcie to zapis w pamięci silnika, przechowujący funkcję wraz z jej środowiskiem

● Środowisko funkcji stanowią wszystkie zmienne zadeklarowane w scope otaczającym funkcję, które zostały wewnątrz niej użyte

● Funkcja ma dostęp do swojego lexical scope’a nawet jeśli jest wywoływana poza swoim scopem

● Domknięcie jest tworzone w miejscu deklaracji funkcji

Podstawowy przykład domknięcia

const name = 'Maciek';

function greet() { return `Hello, my name is ${name}.`;}

const result = greet(); // => "Hello, my name is Maciek."

Prosta funkcja wyższego rzędu

function createGreet() { const name = 'Maciek';

return function() { return `Hello, my name is ${name}.`; };}

const greet = createGreet();const result = greet(); // => "Hello, my name is Maciek."

Factory function

function createPerson(name) { const species = 'Homo Sapiens';

return { showDescription() { return `Person is a ${species} named ${name}.`; }, };}

const john = createPerson('John');john.showDescription(); // => "Person is a Homo Sapiens named John."

Module pattern (ES5)var calculator = function () { function add(a, b) { return a + b; }

function square(a) { return a * a; }

function sumOfSquares(a, b) { return add(aquare(a), square(b)); }

return { sumOfSquares: sumOfSquares, };}();

Partial applicationfunction partial(fn, ...parts) { return function (...rest) { return fn.apply(null, [...parts, ...rest]); };}

function add(...args) { return args.reduce(function (prev, next) { return prev + next; });};

const addFive = partial(add, 5);const addEleven = partial(add, 3, 8);

const result1 = addFive(7); // => 12const result2 = addEleven(3, 6); // => 20

Partial application (arrow functions)

const partial = (fn, ...parts) => (...rest) => fn.apply(null, [...parts, ...rest]);

const add = (...args) => args.reduce((prev, next) => prev + next);

const addFive = partial(add, 5);const addEleven = partial(add, 3, 8);

const result1 = addFive(7); // => 12const result2 = addEleven(3, 6); // => 20

Podsumowanie

● Twórz prosty, modularny, wolny od “magii” kod

● Ograniczaj ruchome części w kodzie, używaj const gdzie to tylko możliwe

● Ograniczaj zakres widoczności zmiennych, organizuj kod w precyzyjne scope'y

● Ukrywaj detale implementacyjne - jedną z metod jest użycie domknięć

● Twórz łatwy w ponownym wykorzystaniu, deklaratywny kod - wykorzystuj zalety funkcji wyższego rzędu i domknięć

Dziękuję za uwagę ;)

[email protected]

brainhub.pl