ES6  – let + const

javascript

Pierwszy temat związany z nadchodzącym ECMAScript 2015, czyli letconst. Oba pojęcia bardzo mocno związane są ze scope deklarowanych zmiennych, co postaram się pokazać w porównaniu ze sposobem deklaracji zmiennych za pomocą słowa kluczowego var.


Zobacz całą serię: Let-s talk about ECMAScript 2015


Spójrzmy wpierw jednak na przykład pokazujący użycie var i deklaracji zmiennych znanych z ES5 i wcześniej.

var a = 1;
 
if (1 === a) {
 var b = 2; 
}
 
for (var c = 0; c < 3; c++) {
 // …
}
 
function letsDeclareAnotherOne() {
 var d = 4;
}
 
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // ReferenceError: d is not defined
 
// window
console.log(window.a); // 1
console.log(window.b); // 2
console.log(window.c); // 3
console.log(window.d); // undefined
  1. Zmienna a deklarowana jest jako globalna.
  2. Zmienna b deklarowana jest wprawdzie wewnątrz bloku if, ale jest ona również widoczna poza nim.
  3. Podobnie jak deklaracja b, zmienna c deklarowana jest w bloku for, który nie ogranicza jej swoim zasięgiem.
  4. Dopiero zmienna d posiada swój własny scope, ponieważ ograniczona jest ciałem funkcji.
Variables in JavaScript are hoisted to the top!

Hoisting, to w JavaScript domyślne zachowanie w momencie deklaracji zmiennych = wynoszenie dekleracji na samą górę skryptu lub ciała funkcji.

Tym samym zmienna w języku JavaScript może być użyta zanim zostanie zainicjalizowana. Różnicę w dekleracji zmiennych, a ich inicjalizacji przedstawia poniższy przykład:

var n = 1;
 
(function () {
 console.log(n);
 var n = 2;
 console.log(n);
})();
 
console.log(n);

let

Spójrzmy teraz na nowy sposób deklaracji zmiennych, który bezpośrednio wpływa na ich zakres działania.

let a = 1;
 
if (1 === a) {
 let b = 2; 
}
 
for (let c = 0; c < 3; c++) {
 // …
}
 
function letsDeclareAnotherOne() {
 let d = 4;
}
 
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
console.log(c); // ReferenceError: c is not defined
console.log(d); // ReferenceError: d is not defined
 
// window
console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined
console.log(window.d); // undefined

Jest to ten sam przykład kodu, który podawałem wyżej dla ES5. Tym razem jednak widzimy różnice w wartościach, które otrzymamy w konsoli przeglądarki. Tylko zmienna a zadeklarowana została jako globalne, a wszystkie pozostałe mają zasięg blokowy.

const

W przypadku użycia const otrzymamy dokładnie takie same wyniki jak dla deklaracji zmiennych z użyciem słowa kluczowego let. const tym samym również pozwala deklarować zmienne o zasięgu blokowym. Różni się jednak od let (i var również) tym, że raz zainicjalizowana wartość zmiennej nie może zostać nadpisana. Można było się tego domyślać po nazwie const (stała).

{
 const PI = 3.141593;
 PI = 3.14; // throws “PI” is read-only
}
 
console.log(PI); // throws ReferenceError: PI is not defined

Każda próba nadpisania zmiennej utworzonej za pomocą słówka const skończy się rzuceniem wyjątku przez interpeter JavaScript.

Powyższy przykład w ES5 mógłby wyglądać następująco:

var PI = (function () {
 var PI = 3.141593;
 return function () { return PI; };
})();

70-480 – Określanie zakresu obiektów i zmiennych

70-480

Definiowanie okresu istnienia zmiennych; utrzymywanie obiektów poza globalną przestrzenią nazw; stosowanie słowa kluczowego „this” w celu odwołania się do obiektu, który wyzwolił zdarzenie; lokalne i globalne ustalanie zakresu zmiennych.



Kolejnym przystankiem na drodze do certyfikatu 70-480 jest znajomość zakresu zmiennych (scope) w JavaScript oraz słowo kluczowe “this”.

Scope i zakres zmiennych

W JavaScript jest nieco inaczej.. a jak! Mam na myśli sytuację, gdzie w wielu innych językach programowania zasięgiem zmiennej jest po prostu zasięg blokowy (metoda, klasa, pętla). W JavaScript zasięg zmiennej definiowany jest poprzez zakres funkcji.

Dlatego żaden inny zakres blokowy {} nie powoduje utworzenia lokalnej zmiennej. Spójrzmy na na naiwny przykład pętli while:

var invoke = true;

while (invoke) {
    var n = 10;
    invoke = false;    
}

console.log(n);

Wykona się ona dokładnie raz i utworzy w swoim bloku zmienną n i przypisze do niej wartość 10. Jaki będzie wynik na konsoli? Otóż 10! Zmienna zadeklarowana została globalnie, a nie lokalnie.

Dopiero utworzenie zmiennej wewnątrz ciała funkcji stworzy na scope lokalny i tym samym zmienne zadeklarowana zostanie lokalnie:

function invoke() {
    var n = 10;
    console.log('1: ' + n);
}

invoke();
console.log('2: ' + n);

Na konsoli ukarze się nam:

> 1: 10
> Uncaught ReferenceError: n is not defined

Zmienna n nie jest widoczna poza zakresem funkcji!

Ciekawy jest przypadek, którego używam na rozmowach kwalifikacyjnych i męczę nim nieco kandydatów:

var n = 1;
 
(function printSomething() {
    console.log('1: ' + n);
    var n = 2;
    console.log('2: ' + n);
})();

console.log('3: ' + n);

Potraficie powiedzieć, co oraz w jakiej kolejności pojawi się na konsoli przeglądarki bez uruchamiania tego kodu? Zostawiam to Wam, a dodam tylko, że spowodowane jest to poprzez: JavaScript Declarations are Hoisted.

Istnieje również zmodyfikowana wersja tego przypadku, ale tutaj analizę również zostawiam Wam:

var n = 1;
 
(function printSomething() {
    n = 2;
    console.log('1: ' + n);
    var n = 3;
    console.log('2: ' + n);
})();

console.log('3: ' + n);

“this” is this!

Skoro już wiemy czym jest scope i jak jest definiowany, to możemy przejść do definicji słowa kluczowego “this”. Definiujemy this globalne oraz zależne od scope, ale zawsze ważny jest tutaj kontekst i miejsce wywołania.

Kontekst globalny

console.log(this.document === document); // true

console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

Kontekst globalny odwołuje się bezpośrednio do obiektu window. Nasze this w tym wypadku to tak naprawdę referencja do tego obiektu. Tym samym przypisując coś do this w scope globalnym, przypisujemy tą wartość do obiektu window.

Kontekst funkcji

Spójrzmy na dwa przykłady i zastanówmy się nad wynikiem uruchomienia poszczególnych kawałków kodu:

function f1() {
  return this;
}

f1() === window; // true
function f2() {
  'use strict';
  return this;
}

f2() === undefined; // true

this w kontekście funkcji zależy od sposobu i miejsca w jakim dana funkcja zostanie wykonana. Używanie tak zwanego strict mode pozwala nam uniknąć problemu z niewłaściwym użyciem this w takim wypadku.

By this miało w powyższym przykładzie jakieś znacznie, powinniśmy stworzyć nowy obiekt za pomocą konstruktora i słowa new.

function f1() {
  this.echo = 'Hello World!';
  return this;
}

console.log(new f1()); // { echo: "Hello World!" }

Kontekst obiektu

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

W tym wypadku this odnosi się do tego samego scope ograniczonego przez nawiasy klamrowe. Idealnie zilustruje to kolejny przykład:

var o = {
  prop: 37,
  p: {
    prop: 38,
    f: function() {
      return this.prop;
    }
  }
};

console.log(o.p.f()); // logs 38

W wyniku otrzymujemy 38, które zdefiniowane w tym samym scope, co funkcja f.

Następny przykład ilustruje jak ważny jest kontekst wywołania funkcji, o czym była już mowa wyżej.

var o = {
    prop: 37
};

function independent() {
  return this.prop;
}

console.log(independent()); // undefined

window.prop = 36;

console.log(independent()); // 36

o.f = independent;

console.log(o.f()); // 37

Funkcje call i apply

By ściśle zdefiniować kontekst wywołania funkcji, a tym samym this w ciele funkcji możemy wykorzystać funkcje call lub apply. Spójrzmy na przykład:

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {
    a: 1,
    b: 3
};

console.log(add.call(o, 5, 7)); // 1 + 3 + 5 + 7 = 16

console.log(add.apply(o, [10, 20])); // 1 + 3 + 10 + 20 = 34

Pierwszym argumentem obu funkcji jest obiekt, który w ciele funkcji użyty będzie jako this. Różnica w obu funkcjach polega na przekazywaniu pozostałych parametrów. W przypadku funkcji call parametry podawane są po prostu po przecinku, a funkcja apply przyjmuje jako drugi argument tablicę, w której znajdują się argumenty wywołania funkcji.

Funkcja bind

Kolejnym sposobem na przypisanie kontekstu this do ciała funkcji jest metoda bind (wprowadzona w ECMAScript 5). Jej wywołanie powoduje, że kontekst this przypisywany jest do danej funkcji na stałe. I kolejny przykład obrazujący tą zależność.

function f() {
  return this.a;
}

var g = f.bind({
    a: 'Hello World!'
});

console.log(g()); // Hello World!

var o = {
    a: 37,
    f: f,
    g: g
};

console.log(o.f(), o.g()); // 37, "Hello World!"

Elementy DOM

Ostatni już przykład wykorzystania słowa kluczowego this. Odwołam się w tym miejscu do ciała funkcji wywołanej w ramach zdarzenia DOM.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/ah4edp7d/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Funkcja echo posiada thiw postaci referencji do klikniętego boxa. Możemy w ten prosty sposób manipulować obiektem DOM.

Część przykładów została zaczerpnięta z MDN.

AngularJS #4 – Wprowadzenie do filtrów

AngularJS



W wyniku przedświątecznej gorączki i mojego “niedoczasu” nie dałem rady postarać się o kolejny wpis w zeszłym tygodniu. Tym razem chciałbym zrobić mały wstęp do filtrów w AngularJS.

W planach na kolejne artykuły o AngularJS są dyrektywy, serwisy, providery, factory oraz wiele innych, ale warto także poznać te podstawowe mechanizmy. Jednym z nich jest własnie serwis $filter lub jak kto woli ngFilter. Filtrów, tych wbudowanych, jak i tych własnych używać możemy po stronie widoków w naszym HTML, ale zarówno w kontrolerach. Służą one do modyfikowania wartości zdefiniowanych w $scope lub właśnie filtrowania kolekcji bez wpływu na ich rzeczywistą wartość.W widokach ich użycie sprowadza się do postawienia pipeline zaraz za wartością, po którym podajemy nazwę filtra, który chcemy wykorzystać. W samych kontrolerach jest to wstrzykiwany serwis $filter, który pozwala na wywołanie konkretnego filtra poprzez parametry funkcji.

Użycie filtrów przedstawia się następująco:

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/5/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

AngularJS posiada listę wbudowanych filtrów, które możemy używać wraz z samym tylko frameworkim. Nie jest ona imponująca, ale twórcy dali nam mechanizm do tworzenia własnych filtrów, który często się przydaje podczas pisani aplikacji z wykorzystaniem AngularJS.

Napiszę tym samym prosty filtr, który pozwoli dzielić wpisaną w pole input wartość na pojedyncze znaki i wyświetlać je z podkreśleniem jako separatorem.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/6/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Tym prostym sposobem możemy pisać własne transformery, dekoratory, czy po prostu filtry.

Zapraszam jednocześnie do obejrzenia kolejnego odcinka z http://egghead.io

[youtube_sc url=”https://www.youtube.com/watch?v=FX5TwV2ZKqc”]

AngularJS #3 – Pierwsza metoda w naszym $scope

AngularJS



W poprzednim wpisie opisywałem zasadę działania dyrektywy ng-controller obejmującej zakres HTML, który otoczony jest przez element, na którym następuje wywołanie owej dyrektywy. Sama wartość dyrektywy wskazuje na kontroler w JavaScript, który odpowiada za jej działanie. W kontrolerze można tworzyć zmienne oraz metody, które dostępne są po stronie HTML.

Zmienna

Najprostszą formą wykorzystania kontrolera jest utworzenie zmiennej, która przypisana jest do $scope. Sam $scope wstrzykiwany jest do kontrolera poprzez argument funkcji. Jako, że jest to element obsługiwany bezpośrednio przez kompilator AngularJS wystarczy, że użyjemy nazwy $scope i tym samym Angular wie, że należy w tym miejscu posłużyć się serwisem $injector do utworzenia nowego $scope, który dziedziczy po $rootScope (dokładny opis znajduje się w oficjalnej dokumentacji AngularJS).

Posłużę się tutaj przykładem z poprzednich wpisów. Tworzymy kontroler APP.ApplicationCtrl (to pełna nazwa, którą używamy po stronie HTML), gdzie zdefiniowana zostaje zmienna name w $scope kontrolera. Następnie jest ona używa w postaci wywołania {{ name }} w HTML w ramach konkretnego div, gdzie zdefiniowany został kontroler ng-controller=”APP.ApplicationCtrl”. $scope jest w tym momencie jakby naszą przestrzenią globalną (dla div), w której widoczne są wszystkie zdefiniowane zmienne lub funkcje / metody. Zamiast więc pisać {{ $scope.name }} wystarczy po prostu {{ name }}.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/snL46/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Metoda

Kolejnym krokiem jest zdefiniowanie w naszym $scope kontrolera – konkretnej funkcjonalności w postaci metody. Metoda ta będzie niczym więcej niż funkcją … przypisaną do zmiennej w $scope. Stworzyłem tym samym w kontrolerze funkcję sayHelloWorld, która pod zmienną $scope.name podstawia nową wartość. W samym HTML dodałem button, w którym to użyłem dyrektywy ng-click i jako jej parametr przekazuję wywołanie funkcji sayHelloWorld(). W tym miejscu nie używam klamer {{ … }} ponieważ przekazują wywołanie funkcji bezpośrednio do dyrektywy ngClick. Tym oto prostym sposobem nasza mini aplikacja otrzymała pierwszą funkcjonalność.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/3/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Dodajmy dwie kolejne funkcje, które zaktualizują wartość zmiennej name. Warto przy tym zauważyć, że nasz widok HTML aktualizuje się automatycznie. Nie wymaga to odświeżania strony, a ilość kodu JavaScript, który musimy napisać także ogranicza się do minimum.

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/EGW5w/4/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Przykład tworzenia i użycia

Po bardziej rozbudowany przykład odsyłam (jak zawsze) do kolejnego filmu z http://egghead.io

[youtube_sc url=”https://www.youtube.com/watch?v=Powr9vzqMac”]

AngularJS #1 – Wprowadzenie

AngularJS



AngularJS to framework JavaScript stworzony przez inżynierów z Google. Służy on do szybkiego i łatwego budowania aplikacji internetowych, tak zwanych – single app. Model oparty o MVW (Model – View – Whatever) pozwala pogodzić idee JavaScript i modelu MVC. Postaram się to Wam pokazać w kolejnych wpisach.

AngularJS – Superheroic JavaScript MVW Framework

HTML na sterydach

To co wyróżnia AngularJS spośród innych tego typu frameworków, to własny kompilator HTML. Pozwala nam on “nauczyć” naszego HTML nowych sztuczek, zachowań i dodać mu kilka funkcjonalności. W ten sposób jesteśmy w stanie zbudować dynamiczną web aplikację.

Two Way Data-Binding

Najlepszym sposobem na oddanie zasady działania będzie oficjalny schemat:

Two Way Data-Binding

Podwójne bindowanie pozwala na automatyczną synchronizację danych, które mamy po stronie widoku, a naszym kontrolerem / modelem danych po stronie JavaScript.

Ciężko opisać ten mechanizm słowami, dlatego zapraszam do pierwszej lekcji kursu na http://egghead.io/ (polecam!), która pokazuje ten element w działaniu.

[youtube_sc url=”https://www.youtube.com/watch?v=Lx7ycjC8qjE”]

AngularJS $scope

$scope to swego rodzaju obiekt, w którym przechowane są dane tworzone przez kontroler. Dane układane są hierarchicznie. Weźmy za przykład następujący kod HTML:

<div ng-controller="ParentController">
	<h1>{{ title }}<h1>

	<div ng-controller="ChildController">
		<h2>{{ heading }}<h2>
	</div>
</div>

Tym samym mamy dwa div-y. Jeden osadzony w środku drugiego. Za ten zewnętrzny odpowiada kontroler ParentController, który wyglądać może tak:

var ParentController = function ($scope) {
	$scope.title = 'Parent Controller';
};

ChildController w środku prezentuje się następująco:

var ChildController = function ($scope) {
	$scope.heading = 'Child Controller';
};

Oba jako argument funkcji przyjmują zmienną $scope, czyli specjalny obiekt AngularJS, który wstrzykiwany jest do funkcji na etapie kompilacji. O tyle, o ile ChildController w swoim $scope posiada zmienną title z kontrolera ParentController, o tyle ParentController nie ma dostępu do zmiennej heading z ChildControllera ponieważ jest jego kontrolerem nadrzędnym.

O samym $scope można pisać sporo. Jestem pewien, że kolejne wpisy przybliżą ten element frameworka znacznie lepiej.

Dyrektywa?!

Dyrektywa, to właśnie sposób na nauczenie HTML nowych sztuczek. Jedną już poznaliśmy. Mam tutaj na myśli ng-controller, który wygląda jak zwykły atrybut HTML. AngularJS podczas fazy interpretacji kodu HTML rozpoznaje takie elementy i buduje dla nich $scope, w który to wstrzykiwany jest kod odpowiedzialny za obsługę dyrektywy. Tym samym jesteśmy w stanie tworzyć dyrektywy, które mogą być atrybutami istniejących tagów HTML, nazwami klas CSS, nowymi tagami HTML, czy zwykłymi komentarzami. Na początku może to wyglądać na nieco skomplikowane i niepotrzebne, ale zapewniam, że po bliższym poznaniu zrobi się wręcz niezbędnym i ulubionym sposobem na tworzenie logiki biznesowej dla naszego HTML.

Hello {{ name }}!

Czas na pierwszy swoisty przykład działania. Posłużę się w tym celu JSFiddle, w którym to ładuję kod AngularJS, tworzę bardzo prosty HTML i dodaję kontroler ApplicationCtrl, gdzie ustawiam wartość dla zmiennej name. W samym HTML pobieram (binduję) wartość zmiennej {{ name }} ze $scope naszego kontrolera. Ot co, proste Hello World!

[wp-js-fiddle url="https://jsfiddle.net/mrzepinski/snL46/7/" style="width:100%;height:400px;border:solid #4173A0 1px;"]

Podsumowanie

Rosnąca popularność AngularJS nie jest przypadkiem. AngularJS daje nam możliwość modułowej budowy naszej aplikacji, która testowana może być w równie łatwy sposób, co samo pisanie kodu biznesowego. AngularJS nie trzeba się uczyć od razu w całości. Próg wejścia jest stosunkowo niski, a w miarę rozbudowy naszego projektu / aplikacji możemy poznawać kolejne funkcjonalności i elementy frameworka. Nasza aplikacja otrzymuje tchnienie życia, a my możemy się świetnie bawić pisząc kod JavaScript. Tak, JavaScript.

Zachęcam do nauki i do czytania kolejnych wpisów.