Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

433 views

Published on

As presented at DevDuck #1 - JavaScript meetup for developers (www.devduck.pl)

Prezentacja z #1 spotkania DevDuck'a w Gliwicach (www.devduck.pl), spotkania mającego na celu poszerzanie wiedzy i wymianę doświadczeń z zakresu szeroko pojętego JS'a, a w szczególności Node.js/React.js i im pochodnych kończących się na ".js" :).
----
Spotkanie odbyło się w Gliwicach w siedzibie Brainhub (www.brainhub.eu)

Published in: Software
  • Login to see the comments

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

  1. 1. Lexical Scope, Function vs. Block Scope, Hoisting, Scope Closures DevDuck |
  2. 2. Maciej Cąderek Fullstack JS Developer @Brainhub
  3. 3. Czym jest scope? Zestaw reguł definiujących zakres widoczności (dostępności) zmiennych.
  4. 4. 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)
  5. 5. 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 }
  6. 6. 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
  7. 7. Czym jest hoisting? ● Dwie fazy przetwarzania kodu - kompilacja i wykonanie var a = 1; var myFunction = function () { // code... }; function myOtherFunction() { // code... } myFunction(); myOtherFunction();
  8. 8. 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
  9. 9. 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()); // => 3 console.log(a); // => 1
  10. 10. 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... }
  11. 11. 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... }
  12. 12. 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
  13. 13. 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
  14. 14. 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)
  15. 15. 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; }
  16. 16. 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; }
  17. 17. 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; } }
  18. 18. 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; })(); }
  19. 19. 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
  20. 20. 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
  21. 21. Podstawowy przykład domknięcia const name = 'Maciek'; function greet() { return `Hello, my name is ${name}.`; } const result = greet(); // => "Hello, my name is Maciek."
  22. 22. 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."
  23. 23. 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."
  24. 24. 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, }; }();
  25. 25. Partial application function 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); // => 12 const result2 = addEleven(3, 6); // => 20
  26. 26. 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); // => 12 const result2 = addEleven(3, 6); // => 20
  27. 27. 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ęć
  28. 28. Dziękuję za uwagę ;) maciej@brainhub.pl brainhub.pl

×