From 68684cb33c2ca61de9a05d4955cc63e8a5557aed Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 26 Mar 2011 19:55:00 -0500 Subject: [PATCH 1/5] fix server path options --- server.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index fb64b73d..3cc5b704 100644 --- a/server.js +++ b/server.js @@ -2,7 +2,12 @@ var build = require('./build').build, qs = require('querystring'), port = 9900, - repoURL = "https://github.com/cramerdev/JavaScript-Garden"; + repoURL = "https://github.com/cramerdev/JavaScript-Garden", + options = { dir: 'doc', pathPrefix: '', template: 'garden.jade', out: 'site'}; + +// FIXME: this is done twice, once when the module loads, and once here +// (with the correct options) +build(options); require('http').createServer(function (request, response) { var payload = ''; @@ -16,7 +21,7 @@ require('http').createServer(function (request, response) { console.log(payload); payload = JSON.parse(qs.parse(payload).payload); if (payload.repository.url === repoURL) { - build(); + build(options); } else { response.writeHead(400); // Bad Request } From 95cf32fc40e0a2a610e43037845e062ba89bf26d Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 4 Oct 2011 09:43:26 -0500 Subject: [PATCH 2/5] add site/pl --- site/pl/index.html | 1914 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1914 insertions(+) create mode 100644 site/pl/index.html diff --git a/site/pl/index.html b/site/pl/index.html new file mode 100644 index 00000000..d539b1b2 --- /dev/null +++ b/site/pl/index.html @@ -0,0 +1,1914 @@ +JavaScript Garden +

Wstęp

JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych +częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych +błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które +niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego +języka.

+ +

JavaScript Garden nie ma na celu nauczyć Cię języka JavaScript. Podstawowa +wiedza na temat języka jest wymagana do zrozumienia zagadnień poruszanych w tym +przewodniku. Aby nauczyć się podstaw jezyka JavaScript, odwiedź znakomity +przewodnik na stronach Mozilla Developer Network.

Licencja

JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje +się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę +problem lub rozwiązag go i zglosić pull request ze swojego repozytorium. +Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.

Obiekty

Wykorzystanie obiektów i ich właściwości

Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami +null oraz undefined.

+ +
false.toString() // 'false'
+[1, 2, 3].toString(); // '1,2,3'
+
+function Foo(){}
+Foo.bar = 1;
+Foo.bar; // 1
+
+ +

Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. +Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +liczby.

+ +
2.toString(); // wyrzuca błąd SyntaxError
+
+ +

Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się +jak obiekt.

+ +
2..toString(); // druga kropka jest poprawnie rozpoznana
+2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
+(2).toString(); // 2 zostanie zewaluowane najpiewr
+
+ +

Obiekty jako typy danych

+ +

Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. +Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +a wartościami dla tych atrybutów.

+ +

Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. +Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz +nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

+ +
var foo = {}; // nowy pusty obiekt
+
+// nowy obiekt z właściwością test o wartości 12
+var bar = {test: 12}; 
+
+ +

Dostęp do właściwości

+ +

Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką +lub notacje z nawiasami kwadratowymi.

+ +
var foo = {name: 'Kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // wyrzuca błąd SyntaxError
+foo['1234']; // działa, zwraca undefined
+
+ +

Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +błędu podczas odczytu nieistniejącej właściwości.

+ +

Usuwanie właściwości

+ +

Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora +delete. Ustawienie własności na undefined lub null usunie tylko wartość +związaną z własnością, ale nie usunie to klucza (nazwy własności) z obiektu.

+ +
var obj = {
+    bar: 1,
+    foo: 2,
+    baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+        console.log(i, '' + obj[i]);
+    }
+}
+
+ +

Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz +została usunięta i dlatego nie została wypisana.

+ +

Notacja właściwości

+ +
var test = {
+    'case': 'I am a keyword so I must be notated as a string',
+    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
+};
+
+ +

Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript +powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

+ +

Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać +zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie +to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

Prototyp

JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +dziedziczenie jest realizowane poprzez prototypy.

+ +

Choć jest to często uważane za jedną ze słabości języka JavaScript, +prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego +modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym +jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

+ +

Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym +językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby +dostosować się do różnic pomiędzy tymi dwoma modelami.

+ +

Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą +tak zwanych łańcuchów prototypów.

+ + + +
function Foo() {
+    this.value = 42;
+}
+Foo.prototype = {
+    method: function() {}
+};
+
+function Bar() {}
+
+// Ustawienie prototypu Bar na nową instancję Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Upewniamy się, że Bar jest ustawiony jako rzeczywisty konstruktor
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // tworzymy nową instancję Bar
+
+// The resulting prototype chain
+test [instance of Bar]
+    Bar.prototype [instance of Foo] 
+        { foo: 'Hello World' }
+        Foo.prototype
+            { method: ... }
+            Object.prototype
+                { toString: ... /* etc. */ }
+
+ +

W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. +Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, +która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do +właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. +Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, +ale wykorzystuje instancje, którą jest przypisana do własności prototype. +Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

+ + + +

Wyszukiwanie własności

+ +

Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha +prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

+ +

Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype +i nadal nie znajdzie określonej właściwości, to zwróci wartość +undefined.

+ +

Właściwość prototype

+ +

Podczas gdy właściwość prototype jest używana przez język do budowania łańcucha +prototypów, istnieje możliwość przypisania do niej dowolnej wartości. Jednakże +prymitywne typy będą po prostu ignorowanie, jeżeli zostaną ustawione jako prototype.

+ +
function Foo() {}
+Foo.prototype = 1; // nie ma wpływu
+
+ +

Przypisywanie obiektów, jak pokazano w powyższym przykładzie, zadziała i pozwala +na dynamiczne tworzenie łańcuchów prototypów.

+ +

Wydajność

+ +

Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć +negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu +do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

+ +

Również, podczas iteracji po właściwościach obiektu +każda właściwość, która znajduje się w łańcuchu prototypów niezależnie +na jakim znajduje się poziomie zostanie wyliczona.

+ +

Rozszerzanie natywnych prototypów

+ +

Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z +najczęściej używanych niedoskonałej częsci języka JavaScript.

+ +

Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. +Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich +niestandardowych funkcjonalności.

+ +

Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
+funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

+ +

Wnioski

+ +

Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie +należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń +aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być +rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami +JavaScript.

hasOwnProperty

W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie +w łańcuchu prototypów niezbędne jest skorzystanie z metody +hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

+ + + +

hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach +i nie przegląda całego łańcucha prototypów.

+ +
// Zatrucie Object.prototype
+Object.prototype.bar = 1; 
+var foo = {goo: undefined};
+
+foo.bar; // 1
+'bar' in foo; // true
+
+foo.hasOwnProperty('bar'); // false
+foo.hasOwnProperty('goo'); // true
+
+ +

Tylko hasOwnProperty da prawidłowy i oczekiwany rezultat. Jest to istotne podczas +iteracji po właściwościach obiektu. Nie ma innego sposobu na ominięcie +właściwości, która nie została zdefiniowana przez ten konkretny obiekt, +ale gdzieś indziej w łańcuchu prototypów.

+ +

hasOwnProperty jako właściwość

+ +

JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje +możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

+ +
var foo = {
+    hasOwnProperty: function() {
+        return false;
+    },
+    bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // zawsze zwraca false
+
+// Została użyta metoda innego obiektu i wywołana z konkekstem 
+// `this` ustawionym na foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+ +

Wnioski

+ +

Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym +obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część +każdej pętli for in, pozwoli to uniknąć błędów pochodzących z +rozszerzonych natywnych prototypów.

The for in Loop

Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów +podczas iteracji po właściwościach obiektu.

+ + + +
// Zatrucie Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+    console.log(i); // wyświetla obie właściwości: bar i moo
+}
+
+ +

Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne +jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając +z metody hasOwnProperty z Object.prototype.

+ + + +

Korzystanie z hasOwnProperty do odfiltrowania

+ +
// foo z przykładu powyżej
+for(var i in foo) {
+    if (foo.hasOwnProperty(i)) {
+        console.log(i);
+    }
+}
+
+ +

To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie +hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty +kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype +zostanie rozszerzony.

+ +

Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje +takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in +metody hasOwnProperty gwarantuje błędy w wykonaniu.

+ +

Wnioski

+ +

Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne +prototypy zostały rozszerzone czy nie.

Funkcje

Deklaracje funkcji i wyrażenia funkcyjne

Funcje w języku JavaScript są typami pierwszoklasowymi. Co oznacza, że mogą +być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy +jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie +asynchronicznej funkcji.

+ +

Deklaracja funckcji

+ +
function foo() {}
+
+ +

Powyższa funkcja zostaje wyniesiona zanim program wystartuje, dzięki temu +jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana, +nawet jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

+ +
foo(); // Działa ponieważ definicja funkcji została wyniesiona 
+       // na początek zasięgu przed uruchomieniem kodu
+function foo() {}
+
+ +

Wyrażenie funkcyjne

+ +
var foo = function() {};
+
+ +

Ten przykład przypisuje nienazwaną i anonimową funkcję do zmiennej foo.

+ +
foo; // 'undefined'
+foo(); // wyrzuca błąd TypeError
+var foo = function() {};
+
+ +

Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu, +zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt +będzie wykonywany.

+ +

Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość foo będzie +ustawiona na domyślną wartość undefined zanim powyższy kod +zostanie uruchomiony.

+ +

Nazwane wyrażenia funkdyjne

+ +

Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.

+ +
var foo = function bar() {
+    bar(); // Działa
+}
+bar(); // wyrzuca ReferenceError
+
+ +

W zewnętrznym zakresie bar nie będzie dostępne, ponieważ funkcja zostaje +przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna. +Jest to spowodowane tym, jak działa rozwiązywanie nazw +w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym +zakresie tej funkcji.

Jak działa this

JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna +nazwa this, niż większość innych języków programowania. Istnieją dokładnie +pięć różnych sytuacji w których wartość this zostaje przypisana w języku JavaScript.

+ +

JavaScript has a different concept of what the special name this refers to +than most other programming languages do. There are exactly five different +ways in which the value of this can be bound in the language.

+ +

Zasięg globalny

+ +
this;
+
+ +

Używanie this w globalnym zasięgu, zwróci po prostu referencje do obiektu global.

+ +

Wywołanie funkcji

+ +
foo();
+
+ +

Tutaj this również będzie wkazywało na obiekt global

+ + + +

Wywoływanie metody

+ +
test.foo(); 
+
+ +

W tym przypadku this będzie wskazywało na test.

+ +

Wywołanie konstruktora

+ +
new foo(); 
+
+ +

Wywołanie funkcji, które jest poprzedzone słowem kluczowym new zachowuje się +jak konstruktor. Wewnątrz funkcji this będzie +wskazywało na nowo utworzony obiekt.

+ +

Jawne ustawienie this

+ +
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // tablica zostanie zamieniona w to co poniżej
+foo.call(bar, 1, 2, 3); // rezultat a = 1, b = 2, c = 3
+
+ +

Używając metod call lub apply z prototypu Function.prototype, wartość this +wewnątrz wołanej funkcji zostanie jawnie ustawiona na pierwszy argument przekazany +podczas wywołania tych metod.

+ +

Zatem w powyższym przykładzie przypadek Wywoływanie metody nie będzie miał +miejsca i this wewnątrz foo będzie wskazywać na bar.

+ + + +

Częste pułapki

+ +

Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być +traktorany jako błąd podczas projektowania języka i nigdy nie wykorzystywany +w praktyce.

+ +
Foo.method = function() {
+    function test() {
+        // wewnątrz tej funkcji this wskazuje na obiekt global
+    }
+    test();
+}
+
+ +

Powszechnym nieporozumieniem jest, że this wewnątrz test wskazuje na Foo, +podczas gdy w rzeczywistości tak nie jest.

+ +

Aby uzyskać dostęp do Foo wewnątrz test niezbędne jest stworzenie wewnątrz +metody lokalnej zmiennej, która będzie wskazywała na Foo.

+ +
Foo.method = function() {
+    var that = this;
+    function test() {
+        // Należy używać that zamiast this wewnątrz tej funkcji
+    }
+    test();
+}
+
+ +

that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać +wartość zewnętrznego this. W połączeniu z domknięciami(closures) +jest to sposób na przekazywanie wartości this wokoło.

+ +

Metody przypisywania

+ +

Kolejną rzeczą, która nie działa w języku JavaScript jest nadawanie aliasów +funkcjom, co oznacza przypisanie metody do zmiennej.

+ +
var test = someObject.methodTest;
+test();
+
+ +

Podobnie jak w pierwszym przypadku test zachowuje się jak wywołanie zwykłej +funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.

+ +

Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem, +to w rzeczywistości jest to rzecz, która powoduje że +dziedziczenie prototypowe działa.

+ +
function Foo() {}
+Foo.prototype.method = function() {};
+
+function Bar() {}
+Bar.prototype = Foo.prototype;
+
+new Bar().method();
+
+ +

Kiedy metoda method zostanie wywołana na instancji Bar, this będzie +wskazywało właśnie tą instancję.

Domknięcia i referencje

Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia, +oznacza 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 +funckję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

+ +

Emulowanie prywatnych zmiennych

+ +
function Counter(start) {
+    var count = start;
+    return {
+        increment: function() {
+            count++;
+        },
+
+        get: function() {
+            return count;
+        }
+    }
+}
+
+var foo = Counter(4);
+foo.increment();
+foo.get(); // 5
+
+ +

Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funckję get. +Obie te funkcję trzymają referencję do zasięgu Counter a co za tym idzie +zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana +w zasięgu tych funkcji.

+ +

Dlaczego zmienne przywatne działają

+ +

Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to +nie istnieje sposób aby uzyskać dostęp do zmiennej count z zewnątrz. +Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną.

+ +
var foo = new Counter(4);
+foo.hack = function() {
+    count = 1337;
+};
+
+ +

Powyższy kod nie zmieni wartości zmiennej count wewnątrz zasięgu Counter, +ponieważ foo.hack nie została zadeklarowana wewnątrz tego konkretnego zasięgu. +Zamiast tego funkcja utworzy lub nadpisze globalną zmienną count.

+ +

Domknięcia wewnątrz pętli

+ +

Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, +aby wartość zmiennej po której odbywa się iteracja był kopiowana do +wewnętrznej funkcji.

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

Powyższy kod nie wypisze numerów od 0 do 9, ale wypisze +dziesięć razy liczbę 10.

+ +

Anonimowa funkcja trzyma wskaźnik do zmiennej i i podczas uruchomienia +console.log, pętla for już zakończyła działanie i wartość zmiennej i +została ustawiona na 10.

+ +

Aby otrzymać zamierzony efekt, niezbędne jest skopiowanie wartości +zmiennej i.

+ +

Unikanie problemu z referencją

+ +

Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać +z anonimowego wrappera.

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

Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem i +jako pierwszym argumentem i otrzyma kopię wartości zmiennej i jako +zmienną e.

+ +

Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada +referencję do zmiennej e, która nie zostanie zmieniona przez pętle for.

+ +

Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic +fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten +wcześniejszy.

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

Obiekt arguments

Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. +Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.

+ + + +

Obiekt arguments nie jest typu Array. Mimo, że posiada pewne cechy +semantyki tablic - właściwość length - to nie dziedziczy on z Array.prototype, +ale w rzeczywistości z Object.

+ +

Ze względu na to nie można używać standardowych dla tablic metod takich jak +push, pop czy slice na obiekcie arguments. Mimo, że iteracja przy pomocy +pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych +należy skonwertować arguments do prawdziwego obiekt Array.

+ +

Konwersja do tablicy

+ +

Poniższy kod zwróci nowy obiekt Array zawierający wszystkie elementy +obiektu arguments.

+ +
Array.prototype.slice.call(arguments);
+
+ +

Jednakże konwersja ta jest wolna i nie jest zalecana w sekcjach, +które mają duży wpływ na wydajność.

+ +

Przekazywanie argumentów

+ +

Zalecany sposób przekazywania argumentów z jednej funkcji do następnej +wyglada następująco.

+ +
function foo() {
+    bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+    // do stuff here
+}
+
+ +

Kolejną sztuczką jest użycie razem call i apply w celu stworzenia +szybkich i nieograniczonych wrapperów.

+ +
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+    console.log(this, a, b, c);
+};
+
+// Stworzenie nieograniczoną wersję metody "method" 
+// która przyjmuje parametry: this, arg1, arg2...argN
+Foo.method = function() {
+
+    // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN)
+    Function.call.apply(Foo.prototype.method, arguments);
+};
+
+ +

Parametry formalne i indeksy argumentów

+ +

Obiekt arguments tworzy funckje getter i setter nie tylko dla swoich +właściwości, ale również dla parametrów formalnych funkcji.

+ +

W rezultacie zmiana wartości parametru formalnego zmieni również wartość +odpowiadającemu mu wpisowi w obiekcie arguments, zachodzi to również w drugą stronę.

+ +
function foo(a, b, c) {
+    arguments[0] = 2;
+    a; // 2                                                           
+
+    b = 4;
+    arguments[1]; // 4
+
+    var d = c;
+    d = 9;
+    c; // 3
+}
+foo(1, 2, 3);
+
+ +

Mity i prawdy o wydajności

+ +

Obiekt arguments jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy +zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów +formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest +używany czy nie.

+ +

Zarówno gettery jak i settery są zawsze tworzone, zatem używanie ich nie ma +praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który +wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu arguments.

+ + + +

Jednakże, istnieje jeden przypadek w którym wydajność drastycznie spada w +nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie +arguments.callee.

+ +
function foo() {
+    arguments.callee; // operowanie na obiekcie funkcji
+    arguments.callee.caller; // i obiekcie funkcji wywołującej
+}
+
+function bigLoop() {
+    for(var i = 0; i < 100000; i++) {
+        foo(); // Normalnie zostałaby wykorzystana metoda inline
+    }
+}
+
+ +

W powyższym przykładzie foo nie może zostać wykorzystana metoda inline +ponieważ potrzebne są nie tylko informacje na własny temat ale również +na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia +inlining i korzyści z niej wynikające, ale również stanowi złamanie +zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu +w jakim została wywołana.

+ +

Mocno zalecane jest aby nigdy nie korzystać z arguments.callee +i żadnej jej własności.

+ +

Konstruktory

Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym new zachowuje się +jak konstruktor.

+ +

Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na +nowo utworzony obiekt Object. Prototyp prototype tego +nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji, +która została wywołana jako konstruktor.

+ +

Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas +fukcja domyślnie zwraca wartość this - nowy obiekt.

+ +
function Foo() {
+    this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+    console.log(this.bla);
+};
+
+var test = new Foo();
+
+ +

Powyżej wywołanya została funkcja Foo jako konstruktor oraz ustawia +nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.

+ +

W tym przypadku jawna deklaracja return w funkcji zwraca wartość +ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest +obiekt Object.

+ +
function Bar() {
+    return 2;
+}
+new Bar(); // nowy obiekt
+
+function Test() {
+    this.value = 2;
+
+    return {
+        foo: 1
+    };
+}
+new Test(); // zwrócony obiekt
+
+ +

Jeżeli słowo kluczowe new zostanie pominięte funkcja nie zwróci nowego +obiektu.

+ +
function Foo() {
+    this.bla = 1; // zostanie ustawiona w obiekcie global
+}
+Foo(); // undefined
+
+ +

Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem this w języku JavaScript to jako +wartość thiszostanie wykorzystany obiekt global.

+ +

Fabryki

+ +

Aby móc ominąć słowo kluczowe new konstruktor musi jawnie zwracać wartość.

+ +
function Bar() {
+    var value = 1;
+    return {
+        method: function() {
+            return value;
+        }
+    }
+}
+Bar.prototype = {
+    foo: function() {}
+};
+
+new Bar();
+Bar();
+
+ +

Oba wywołania Bar zwrócą tą samą rzecz, nowo utworzony obiekt, który posiada +właściwość nazwaną method w sobie, dla którego Bar jest Domknięciem.

+ +

Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na +prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype). +Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak Bar +nidgy nie zwróci tego nowego obiektu Bar, ale literał obiektu, który jest po +słowie kluczowym return.

+ +

W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem +i nieużyciem słowa kluczowego new.

+ +

Tworzenie nowych obiektów korzystając z fabryk

+ +

Często zaleca się nie korzystać z operatora new ponieważ zapominając +go zastosować może prowadzić do błędów.

+ +

W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować +nowy obiekt wewnątrz tej fabryki.

+ +
function Foo() {
+    var obj = {};
+    obj.value = 'blub';
+
+    var private = 2;
+    obj.someMethod = function(value) {
+        this.value = value;
+    }
+
+    obj.getPrivate = function() {
+        return private;
+    }
+    return obj;
+}
+
+ +

Mimo, że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia +korzystanie ze zmiennych prywatnych, to posiada +pewne wady. +While the above is robust against a missing new keyword and certainly makes +the use of private variables easier, it comes with some +downsides. + 1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod + poprzez prototyp + 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego + obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp + do nowo utworzonego obiektu. + 3. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe + new jest sprzeczne z duchem języka.

+ +

Wnioski

+ +

Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie +powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to +do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie +ważne jest aby wybrać określony styl tworzenia obiektów i się go trzymać.

Zasięg zmiennych i przestrzenie nazw

Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące +nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego. +Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.

+ +
function test() { // definiuje zasięg (scope)
+    for(var i = 0; i < 10; i++) { // nie definiuje zasięgu (scope)
+        // count
+    }
+    console.log(i); // 10
+}
+
+ + + +

W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest +definiowane w jednej globalnie współdzielonej przestrzeni nazw.

+ +

Za każdym razem gdy zmienna jest +Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd ReferenceError.

+ +

Zmora globalnych zmiennych

+ +
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
+ +

Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienna +nazwaną foo w globalnym zasięgu natomiast skrypt B definiuje foo +w aktualnym zasięgu.

+ +

Jeszcze raz, to wcale nie daje tego samego efektu, nie użycie var może mieć +poważne konsekwencje.

+ +
// globalny zasięg
+var foo = 42;
+function test() {
+    // lokalny zasięg
+    foo = 21;
+}
+test();
+foo; // 21
+
+ +

Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość +zmiennej globalnej foo. Mimo, że nie wygląda to na początku jak duży problem, +to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z var +wprowadzi straszne i trudne do wyśledzenia błędy.

+ +
// globalny zasięg 
+var items = [/* jakaś lista */];
+for(var i = 0; i < 10; i++) {
+    subLoop();
+}
+
+function subLoop() {
+    // scope of subLoop
+    for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji
+        // do amazing stuff!
+    }
+}
+
+ +

Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ +subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli +for pozwoliło by łatwo uniknąć problemu. Słowo kluczowe var nie powinno być +nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na +zewnętrzny zasięg.

+ +

Lokalne zmienne

+ +

Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry funkcji +oraz zmienne zadeklarowane poprzez deklaracje var wewnątrz funkcji.

+ +
// globalny zasięg
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+    // lokalny zasięg fukcji test
+    i = 5;
+
+    var foo = 3;
+    bar = 4;
+}
+test(10);
+
+ +

Zmienne foo oraz i są lokalnymi zmiennymi wewnątrz zasiegu funkcji test, +natomiast przypisanie wartości do bar nadpisze zmienną globalną o tej samej nazwie.

+ +

"Hoisting" - wywindowanie, podnoszenie

+ +

JavaScript winduje deklaracje. Oznacza to, że zarówno deklaracja ze słowem +kluczowym var jak i deklaracje funkcji function zostaną przeniesione na +początek otaczającego zasięgu.

+ +
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+    if (false) {
+        goo = 1;
+
+    } else {
+        var goo = 2;
+    }
+    for(var i = 0; i < 100; i++) {
+        var e = data[i];
+    }
+}
+
+ +

Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript +przeniesie deklarację zmiennej var oraz deklarację funkcji function na szczyt +najbliższego zasięgu.

+ +
// deklaracje var zostaną przeniesione tutaj
+var bar, someValue; // ustawione domyślnie na 'undefined'
+
+// deklaracje funkcji zostaną również przeniesione na górę
+function test(data) {
+    var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj
+    if (false) {
+        goo = 1;
+
+    } else {
+        goo = 2;
+    }
+    for(i = 0; i < 100; i++) {
+        e = data[i];
+    }
+}
+
+bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined'
+someValue = 42; // przypisania nie zostają zmienione przez 'hoisting'
+bar = function() {};
+
+test();
+
+ +

Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętle, +ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

+ +

W oryginalnym kodzie wewnątrz deklaracja if zdaje się modyfikować zmienną +globalną goo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym +jak zostało zastosowane windowanie (hoisting).

+ +

Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się +wyrzucać błąd ReferenceError.

+ +
// sprawdz czy SomeImportantThing zostało zainicjalizowane
+if (!SomeImportantThing) {
+    var SomeImportantThing = {};
+}
+
+ +

Oczywiście powyższy kod działa ze względu na fakt, że deklaracja var zostanie +przeniesiona na początek globalnego zasięgu.

+ +
var SomeImportantThing;
+
+// inny kod który może ale nie musi zainicjalizować SomeImportantThing
+
+// upewnienie sie że SomeImportantThing zostało zainicjalizowane
+if (!SomeImportantThing) {
+    SomeImportantThing = {};
+}
+
+ +

Kolejność rozwiązywania nazw

+ +

Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają +zdefiniowana wewnątrz specjalną nazwę this, która wskazuje +na aktualny obiekt.

+ +

Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę +arguments, która zawiera listę argumentów przekazaną do +funkcji.

+ +

Na przykład, kiedy próbujemy odczytać zmienną foo wewnątrz zasięgu funkcji, +JavaScript będzie szukać nazwy w określonej kolejności: + 1. Jeżeli wewnątrz aktualnego zasięgu znajduje się deklaracja var foo skorzystaj z niej. + 2. Jeżeli jeden z parametrów fukcji został nazwany foo użyj go. + 3. Jeżeli fukcja została nazwana foo skorzystaj z tego. + 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.

+ + + +

Przestrzenie nazw

+ +

Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest +prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć +tego problemu korzystając z anonimowych wrapperów.

+ +
(function() {
+    // autonomiczna "przestrzeń nazw"
+
+    window.foo = function() {
+        // wyeksponowane domkniecie (closure)
+    };
+
+})(); // natychmiastowe wykonanie funkcji
+
+ +

Nienazwane funkcje są rozpoznane jako wyrażenia, więc +aby mogły zostać wywołane muszą zostać zewaluowane.

+ +
( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów
+function() {}
+) // zwrócenie obiektu funkcji
+() // wywołanie rezultatu ewaluacji
+
+ +

Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że +mają inną składnie, zachowują się dokładnie tak samo.

+ +
// Trzy inne sposoby
+!function(){}();
++function(){}();
+(function(){}());
+
+ +

Wnioski

+ +

Zaleca się aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz +jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale +również wprowadza lepszą modularyzację programów.

+ +

Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. +Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, +który jest podatny na błędy i trudny do utrzymania.

Tablice

Iterowanie po tablicach oraz właściwości tablic

Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania +pętli for in do iteracji po nich. W rzeczywiści istnieje +wiele dobrych powodów przeciwko wykorzystania for in na tablicach.

+ + + +

Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie +hasOwnProperty, ale wówczas pętla staje się +dwadzieście razy wolniejsza od normalnej pętli for.

+ +

Iteracja

+ +

W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy +użyć klasycznej pętli for.

+ +
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+    console.log(list[i]);
+}
+
+ +

Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie +długości tablicy poprzez l = list.length.

+ +

Mimo, że właściwość length jest zdefiniowana w wewnątrz tablicy, istnieje nadal +dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. +Chociaż najnowsze silniki JavaScript mogą zastosować optymalizację w tym +przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników czy też nie.

+ +

W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla +będzie tylko w połowie tak szybka jak ta z buforowaniem długości.

+ +

Właściwość length

+ +

Mimo, że getter właściwości length po prostu zwraca liczbę elementów, które są +zawarte w tablicy, to setter może być użyta do skracania tablicy.

+ +
var foo = [1, 2, 3, 4, 5, 6];
+foo.length = 3;
+foo; // [1, 2, 3]
+
+foo.length = 6;
+foo; // [1, 2, 3]
+
+ +

Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości +length nie ma żadnego wpływu na tablicę.

+ +

Wnioski

+ +

Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for +i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest +znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

Konstruktor Array

Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia +nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje +parametry do niego przekazane.

+ +
[1, 2, 3]; // Rezultat: [1, 2, 3]
+new Array(1, 2, 3); // Rezultat: [1, 2, 3]
+
+[3]; // Rezultat: [3]
+new Array(3); // Rezultat: []
+new Array('3') // Rezultat: ['3']
+
+ +

W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i +ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę +z ustawioną właściwością length na wartość przekazaną jako argument. Należy +zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób, +rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

+ +
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // zwraca false, indeks nie został ustawiony
+
+ +

Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania +pętli for.

+ +
// count - ilosc powtorzen
+// stringToRepeat - ciąg znaków do powtórzenia 
+new Array(count + 1).join(stringToRepeat); 
+
+ +

Wnioski

+ +

W miare możliwości należy unikać używania konstruktora Array. Literały są +zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. +Zwiększają również czytelność kodu.

Typy

Równość i porównania

JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.

+ +

Operator równości

+ +

Operator równości składa się z dwóch znaków "równa się": ==

+ +

JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości +konwertuje typy (dokonuje koercji), aby wykonać porównanie.

+ +
""           ==   "0"           // false
+0            ==   ""            // true
+0            ==   "0"           // true
+false        ==   "false"       // false
+false        ==   "0"           // true
+false        ==   undefined     // false
+false        ==   null          // false
+null         ==   undefined     // true
+" \t\r\n"    ==   0             // true
+
+ +

Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki +porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą +praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

+ +

Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +przekształcony na typ Number przed porównaniem z drugą liczbą.

+ +

Operator ścisłej równości

+ +

Operator ścisłej równości składa się z trzech znaków "równa się": ===

+ +

Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +dokonuje koercji typów przed porównaniem.

+ +
""           ===   "0"           // false
+0            ===   ""            // false
+0            ===   "0"           // false
+false        ===   "false"       // false
+false        ===   "0"           // false
+false        ===   undefined     // false
+false        ===   null          // false
+null         ===   undefined     // false
+" \t\r\n"    ===   0             // false
+
+ +

Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie" +języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań +obiektów o różnych typach.

+ +

Porównywanie obiektów

+ +

Mimo, że oba operatory == i === nazywane są operatorami równościowymi, +to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

+ +
{} === {};                   // false
+new String('foo') === 'foo'; // false
+new Number(10) === 10;       // false
+var foo = {};
+foo === foo;                 // true
+
+ +

Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy +jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator +is w Pythonie i porównanie wskaźników w C.

+ +

Wnioski

+ +

Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy +potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna +być dokonana jawnie a nie pozostawiona trudnym regułom koercji +obowiązującym w języku.

Operator typeof

Operator typeof (razem z operatorem instanceof) jest +prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie +całkowicie wadliwy.

+ +

Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek +nie jest związany z sprawdzaniem typu obiektu.

+ + + +

Tablica typów JavaScript

+ +
Wartość             Klasa      Typ
+-------------------------------------
+"foo"               String     string
+new String("foo")   String     object
+1.2                 Number     number
+new Number(1.2)     Number     object
+true                Boolean    boolean
+new Boolean(true)   Boolean    object
+new Date()          Date       object
+new Error()         Error      object
+[1,2,3]             Array      object
+new Array(1, 2, 3)  Array      object
+new Function("")    Function   function
+/abc/g              RegExp     object (function w Nitro i V8)
+new RegExp("meow")  RegExp     object (function w Nitro i V8)
+{}                  Object     object
+new Object()        Object     object
+
+ +

W powyższej tabeli Typ odnosi się do wartości zwracanej przez operator typeof. +Wyraźnie widać, że zwracane wartości w ogóle nie są spójne.

+ +

Klasa odnosi sie do wartości wewnętrznej właściwości [[Class]] obiektu.

+ + + +

W celu uzyskania wartości właściwości [[Class]] trzeba skorzystać z metody +toString z Object.prototype.

+ +

Klasa obiektu

+ +

Specyfikacja zawiera dokładnie jeden sposób dostepu do wartości [[Class]], +wykorzystując Object.prototype.toString.

+ +
function is(type, obj) {
+    var clas = Object.prototype.toString.call(obj).slice(8, -1);
+    return obj !== undefined && obj !== null && clas === type;
+}
+
+is('String', 'test'); // true
+is('String', new String('test')); // true
+
+ +

Powyższy przykład wywołuje Object.prototype.toString z wartością +this ustawioną na obiekt, dla której wartość właściwości +[[Class]] ma zostać odczytana.

+ + + +

Testowanie niezdefiniowania zmiennej

+ +
typeof foo !== 'undefined'
+
+ +

Powyższy kod sprawdza czy foo została faktycznie zadeklarowana czy też nie. +Próba odwołania się do zmiennej spowodowała by wyrzucenie błędu ReferenceError. +Jest to jedyne praktyczne wykorzystanie operatora typeof.

+ +

Wnioski

+ +

W celu sprawdzenia typu obiektu zalecane jest skorzystanie z +Object.prototype.toString, ponieważ jest to jedyny wiarygodny sposób. Jak +pokazano w powyższej tabeli typów, niektóre wartości zwracane przez typeof nie +są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnych +implementacjach.

+ +

O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została +zdefiniowana, powinien być unikany o ile to tylko możliwe.

Operator instanceof

Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. +Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +typeof.

+ +

Porównywanie obiektów utworzonych klas

+ +
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo
+// a nie faktyczną instancję Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
+
+ +

Stosowanie instanceof na natywnych typach

+ +
new String('foo') instanceof String; // true
+new String('foo') instanceof Object; // true
+
+'foo' instanceof String; // false
+'foo' instanceof Object; // false
+
+ +

Jedną ważną rzeczą, którą należy zauważyć jest to, że instanceof nie zadziała +na obiektach, które pochodzą z różnych kontekstów JavaScript (np. z różnych +dokumentów wewnątrz przeglądarki), ponieważ ich konstruktory nie będą tymi +samymi obiektami.

+ +

Wnioski

+ +

Operator instanceof powinien być tylko używany podczas korzystania z obiektów +klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. +Podobnie jak operator typeof, należy unikać korzystania +z tego operatora w innych sytuacjach.

Rzutowanie typów

JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +typów gdziekolwiek jest to możliwe.

+ +
// These are true
+new Number(10) == 10; // Number.toString() zostanie przekształcone
+                      // z powrotem do liczby
+
+10 == '10';           // Stringi zostaną przekształcone do typu Number
+10 == '+10 ';         // Kolejne wariacje
+10 == '010';          // i następne
+isNaN(null) == false; // null zostanie przekształcony do 0
+                      // który oczywiście nie jest NaN
+
+// Poniższe zwracają false
+10 == 010;
+10 == '-10';
+
+ + + +

Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze +ściełego operatora równości. Mimo, że pozwala to uniknąć wiele +typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego +typowania języka JavaScript.

+ +

Konstruktory typów wbudowanych

+ +

Konstruktory typów wbudowanych takich, jak Number lub String zachowują się +inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

+ +
new Number(10) === 10;     // False, Object i Number
+Number(10) === 10;         // True, Number i Number
+new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
+
+ +

Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt +typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja +Number zachowa się jak konwerter.

+ +

Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą +ilością rzutowań (koercją) typów.

+ +

Najlepszym rozwiązaniem jest jawne rzutowanie do jednego z trzech typów.

+ +

Rzutowanie do typu String

+ +
'' + 10 === '10'; // true
+
+ +

Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String.

+ +

Rzutowanie do typu Number

+ +
+'10' === 10; // true
+
+ +

Zastosowanie unarnego operatora + spowoduje rzutowanie do typu Number.

+ +

Rzutowanie do typu Boolean

+ +

Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana +do typu Boolean

+ +
!!'foo';   // true
+!!'';      // false
+!!'0';     // true
+!!'1';     // true
+!!'-1'     // true
+!!{};      // true
+!!true;    // true
+

Jądro

Dlaczego nie należy używać eval

Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    eval('foo = 3');
+    return foo;
+}
+test(); // 3
+foo; // 1
+
+ +

Niestaty eval zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana +bezpośrednio i nazwa wołanej funkcji równa sie eval.

+ +
var foo = 1;
+function test() {
+    var foo = 2;
+    var bar = eval;
+    bar('foo = 3');
+    return foo;
+}
+test(); // 2
+foo; // 3
+
+ +

Należy unikać stosowania eval o ile to tylko możliwe. W 99.9% przypadków można +osiągnąć ten sam efekt nie używając eval.

+ +

eval w przebraniu

+ +

Funkcje wykonywane po upływie czasu setTimeout i setInterval +mogą przyjąć string jako pierwszy argument. String ten zostanie zawsze wykonany +w globalnym zasięgu, ponieważ funkcja eval zostanie wywołana niebezpośrednio w tym +przypadku.

+ +

Problemy z bezpieczeństwem

+ +

Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ +wykonuje każdy kod, który zostanie do niej przekazany i nie należy nigdy +używać jej na stringach nieznanego lub niezaufanego pochodzenia.

+ +

Wnioski

+ +

Funkcja eval nie powinna być w ogole używana, każdy kod, który ją wykorzystuje +powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. +W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod +należy przemyśleć raz jeszcze i ulepszyć kod aby nie wymagał użycia eval.

undefined i null

JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną +z tych dwóch jest undefined.

+ +

Wartość undefined

+ +

undefined jest typem z dokładnie jedną wartością: undefined.

+ +

Język również definiuje globalną zmienną, która ma wartość undefined, zmienna +ta jest nazwana undefined. Jednakże jest to zmienna a nie stała czy słowo +kluczowe w języku. Oznacza to że możliwe jest nadpisanie wartości tej zmiennej.

+ + + +

Kilka przykładów kiedy wartość undefined jest zwracana:

+ +
    +
  • Dostęp do (niemodyfikowalnej) zmiennej globalnej undefined.
  • +
  • Wyjście z funkcji, która nie ma deklaracji return.
  • +
  • Deklaracja return, która nic jawnie nie zwraca.
  • +
  • Poszukiwanie nieistniejącej właściwości.
  • +
  • Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji
  • +
  • Wszystko co zostało ustawione na wartość undefined
  • +
+ +

Obsługa przypadku zmiany wartości undefined

+ +

Ponieważ globalna zmienna undeined tylko zawiera kopię prawdziwej wartości typu +undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości +typu undefined.

+ +

Jednak, aby porównać coś do wartości undefined potrzebne jest odczytanie wartości +undefined.

+ +

Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej undefined, korzysta +się z powszechnej techniki dodania dodatkowego parametru do +anonimowego wrappera, do którego nie zostanie przekazany +argument.

+ +
var undefined = 123;
+(function(something, foo, undefined) {
+    // undefined lokalnym zasięgu znowu 
+    // odnosi się do poprawnej wartości
+
+})('Hello World', 42);
+
+ +

Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej +wewnątrz wrappera.

+ +
var undefined = 123;
+(function(something, foo) {
+    var undefined;
+    ...
+
+})('Hello World', 42);
+
+ +

Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +kluczowe var i spację po nim.

+ +

Zastosowanie null

+ +

Podczas gdy undefined w kontekście języka jest używany jak null w sensie +tradycyjnych języków, to null w JavaScript (jako literał i jako typ) jest po +prostu kolejnym typem danych.

+ +

Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów +poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go +zastąpić przez undefined.

Automatyczne wstawianie średnika

Mimo, że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania +średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.

+ +

Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript +automatycznie wstawia średniki o ile napotka błąd parsowania związany z +brakiem średnika.

+ +
var foo = function() {
+} // błąd parsowania, oczekiwany był w tym miejscu średnik
+test()
+
+ +

Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt.

+ +
var foo = function() {
+}; // bez błędu parser kontynuuje
+test()
+
+ +

Automatyczne wstawianie średników jest uważane za jeden z największych błędów +konstrukcji języka, ponieważ może ono zachowanie kodu.

+ +

Jak działa wstawianie

+ +

Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach +je wstawi.

+ +
(function(window, undefined) {
+    function test(options) {
+        log('testing!')
+
+        (options.list || []).forEach(function(i) {
+
+        })
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        )
+
+        return
+        {
+            foo: function() {}
+        }
+    }
+    window.test = test
+
+})(window)
+
+(function(window) {
+    window.someLibrary = {}
+
+})(window)
+
+ +

Poniżej znajduje się rezultat "zgadywania" parsera.

+ +
(function(window, undefined) {
+    function test(options) {
+
+        // Nie wstaniony średnik, linie zostały połączone
+        log('testing!')(options.list || []).forEach(function(i) {
+
+        }); // <- wstawiony
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        ); // <- wstawiony
+
+        return; // <- wstawiony, psując deklarację return
+        { // potraktowane jako definicja bloku
+
+            // etykieta oraz pojedyncze wyrażenie
+            foo: function() {} 
+        }; // <- wstawiony
+    }
+    window.test = test; // <- wstawiony
+
+// Kolejna połączona linia
+})(window)(function(window) {
+    window.someLibrary = {}; // <- wstawiony
+
+})(window); //<- wstawiony
+
+ + + +

Parser drastycznie zmienił działanie powyższego kodu, w niektórych przypadkach +zmienił go źle.

+ +

Nawiasy

+ +

W przypadku, gdy w następnej linii znajduje się nawias, parser nie wstawi +średnika.

+ +
log('testing!')
+(options.list || []).forEach(function(i) {})
+
+ +

Ten kod zostanie zmieniony w poniższą jedną linię.

+ +
log('testing!')(options.list || []).forEach(function(i) {})
+
+ +

Jest bardzo prawdopodobne, że log nie zwróci fukcji, co za tym idzie +powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a +function - undefined nie jest funkcją.

+ +

Wnioski

+ +

Zaleca się aby nigdy nie pomijać średników, pozostawiać nawias otwierający +w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji +if / else bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript +przed zmianą działania kod.

Inne

setTimeout i setInterval

Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji korzystając z funkcji setTimeout i setInterval. +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the setTimeout and setInterval functions.

+ + + +
function foo() {}
+var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
+
+ +

Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za +około tysiąc milisekund. foo zostanie wykonana dokładnie jeden raz.

+ +

Nie ma pewności, że kod zaplanowany do wykonania wykona się dokładnie po +upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy +to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, +że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko +jedno wątkowy.

+ +

Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w +globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji +będzie wkazywać na obiekt global.

+ +
function Foo() {
+    this.value = 42;
+    this.method = function() {
+        // this wskazuje na obiekt global
+        console.log(this.value); // wypisze undefined
+    };
+    setTimeout(this.method, 500);
+}
+new Foo();
+
+ + + +

Kolejkowanie wywołań z setInterval

+ +

Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - +jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X +milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

+ +

Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, +setInterval będzie próbować uruchamiać daną funkcję co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się +to wydarzyć przy krótkim interwale.

+ +
function foo(){
+    // coś co blokuje wykonanie na 1 sekundę 
+}
+setInterval(foo, 100);
+
+ +

W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na +jedną sekundę.

+ +

Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne +wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już +w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

+ +

Radzenie sobie z możliwymi blokadami

+ +

Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout +wewnątrz wywoływanej funkcji.

+ +
function foo(){
+    // coś co blokuje wykonanie na 1 sekundę
+    setTimeout(foo, 100);
+}
+foo();
+
+ +

Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega +kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja +foo może zdecydować czy powinna się wywołać ponownie czy też nie.

+ +

Ręczne usuwanie budzików

+ +

Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID +do clearTimeout lub clearInterval, w zależności z jakiej funkcji zostało +zwrócone ID.

+ +
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
+ +

Usuwanie wszystkich budzików

+ +

Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub +interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

+ +
// usunięcie "wszystkich" budzików 
+for(var i = 1; i < 1000; i++) {
+    clearTimeout(i);
+}
+
+ +

Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, +ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute +force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

+ +

Ukryte wykorzystanie eval

+ +

Do setTimeout i setInterval można również przekazać string jako pierwszy +parametr zamiast obiektu funkcji, jednakże nigdy nie należy korzystać z tej +możliwości, ponieważ wewnętrznie setTimeout i setInterval wykorzystują eval.

+ + + +
function foo() {
+    // zostanie wykonane 
+}
+
+function bar() {
+    function foo() {
+        // nigdy nie zostanie wywołane
+    }
+    setTimeout('foo()', 1000);
+}
+bar();
+
+ +

Ponieważ eval nie zostało wywołane wprost w tym przypadku, to +string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, +co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

+ +

Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów +do funkcji, która ma zostać wywołana przez budzik.

+ +
function foo(a, b, c) {}
+
+// NIGDY nie należy tak robić 
+setTimeout('foo(1,2, 3)', 1000)
+
+// Zamiast tego należy skorzystać z anonimowej funkcji
+setTimeout(function() {
+    foo(a, b, c);
+}, 1000)
+
+ + + +

Wnioski

+ +

Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub +setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest +przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i +wewnątrz niej dokonać przekazania argumentów.

+ +

Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może +zablokować wykonanie JavaScriptu.

\ No newline at end of file From 4efb139363faf6f45f6d67a67d6237d65e770939 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 4 Oct 2011 09:45:11 -0500 Subject: [PATCH 3/5] Commit pl index --- site/pl/index.html | 559 ++++++++++++++++++++++----------------------- 1 file changed, 278 insertions(+), 281 deletions(-) diff --git a/site/pl/index.html b/site/pl/index.html index d539b1b2..60974a70 100644 --- a/site/pl/index.html +++ b/site/pl/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

Wstęp

JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych +

Wstęp

JavaScript Garden jest rosnącą kolekcją dokumentów o najdziwniejszych częściach języka JavaScript. Dokumentacja pomaga uniknąć najczęściej popełnianych błędów, sybtelnych bugów, problemów wydajnościowych oraz złych praktyk, na które niedoświadczeni programiści JavaScript mogą natrafić próbując poznać tajniki tego @@ -11,15 +11,16 @@ wiedza na temat języka jest wymagana do zrozumienia zagadnień poruszanych w tym przewodniku. Aby nauczyć się podstaw jezyka JavaScript, odwiedź znakomity przewodnik na stronach Mozilla Developer Network.

Licencja

JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę -problem lub rozwiązag go i zglosić pull request ze swojego repozytorium. +problem lub rozwiąż go i zgloś pull request ze swojego repozytorium. Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.

Obiekty

Wykorzystanie obiektów i ich właściwości

Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami null oraz undefined.

@@ -31,9 +32,9 @@ Foo.bar; // 1 -

Popularnym błędem jest wykorzystanie literałów liczbowych jako obiektu. -Spowodowanie jest to usterką w parserze JavaScript, który interpretuje kropkę -po literale liczbowym jako rozdzielenie części całkowitej od części po przecinku +

Popularnym błędem jest traktowanie literałów liczbowych jak obiektu. +Spowodowane jest to specyfiką parsera JavaScript, który interpretuje kropkę +po literale liczbowym jako rozdzielenie części całkowitej od części ułamkowej liczby.

2.toString(); // wyrzuca błąd SyntaxError
@@ -44,20 +45,20 @@
 
 
2..toString(); // druga kropka jest poprawnie rozpoznana
 2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
-(2).toString(); // 2 zostanie zewaluowane najpiewr
+(2).toString(); // 2 zostanie najpierw zewaluowane
 

Obiekty jako typy danych

-

Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne. -Ponieważ obiekty głównie składają się z mapowań pomiędzy nazwanymi właściwościami (kluczami) +

Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne, +ponieważ obiekty składają się głównie z mapowań pomiędzy nazwanymi właściwościami (kluczami) a wartościami dla tych atrybutów.

-

Używając literału obiektu - notacji {} - istnieje możliwość stworzenie obiektu prostego. +

Używając literału obiektu - notacji {} - istnieje możliwość stworzenia obiektu prostego. Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz -nie bedzie posiadał żadnych własnych właściwości zdefiniowanych w sobie.

+nie bedzie posiadał żadnych własnych właściwości.

-
var foo = {}; // nowy pusty obiekt
+
var foo = {}; // nowy, pusty obiekt
 
 // nowy obiekt z właściwością test o wartości 12
 var bar = {test: 12}; 
@@ -65,8 +66,8 @@
 
 

Dostęp do właściwości

-

Właściwości obiektu można uzyskać na dwa sposoby, poprzez notację z kropką -lub notacje z nawiasami kwadratowymi.

+

Właściwości obiektu można uzyskać na dwa sposoby - poprzez notację z kropką +lub z nawiasami kwadratowymi.

var foo = {name: 'Kitten'}
 foo.name; // kitten
@@ -79,8 +80,8 @@
 foo['1234']; // działa, zwraca undefined
 
-

Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą że notacja z nawiasami -kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzycenia +

Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą, że notacja z nawiasami +kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzucenia błędu podczas odczytu nieistniejącej właściwości.

Usuwanie właściwości

@@ -105,43 +106,43 @@ } -

Powyższy kod wypisuje dwie linie bar undefined i foo null - tylko własność baz +

Powyższy kod wypisuje dwie linie - bar undefined i foo null. Tylko własność baz została usunięta i dlatego nie została wypisana.

Notacja właściwości

var test = {
-    'case': 'I am a keyword so I must be notated as a string',
-    delete: 'I am a keyword too so me' // wyrzuca błąd SyntaxError
+    'case': 'jestem słowem kluczowym, więc muszę być w cudzysłowie',
+    delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError
 };
 
-

Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst(bez cudzysłowów +

Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów lub apostrofów) lub jako string (w cudzisłowach lub apostrofach). -Ze względu na kolejne niedociągnięcie w parserze JavaScript +Ze względu na kolejne niedociągnięcie w parserze JavaScript, powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.

Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie -to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

Prototyp

JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +to poprawnie zinterpretowane przez starsze silniki języka JavaScript.

Prototyp

JavaScript nie posiada klasycznego modelu dziedziczenia. Zamiast tego dziedziczenie jest realizowane poprzez prototypy.

Choć jest to często uważane za jedną ze słabości języka JavaScript, prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego -modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowym -jest dość proste, podczas gdy zrobienie odwrotnie to już o wiele trudniejsze zadanie.

+modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowego +jest dość proste, podczas gdy zrobienie odwrotnego przekształcenie to o wiele trudniejsze zadanie.

Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym -językiem, któy posiada prototypowy model dziedziczenia, to wymaga troche czasu aby -dostosować się do różnic pomiędzy tymi dwoma modelami.

+językiem, któy posiada prototypowy model dziedziczenia, dostosowanie się do różnic pomiędzy +tymi dwoma modelami wymaga trochę czasu.

Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą tak zwanych łańcuchów prototypów.

@@ -173,27 +174,27 @@ { toString: ... /* etc. */ }
-

W powyższym przykładzie obiekt test będzie dziedziczył z obydwu tj. +

W powyższym przykładzie obiekt test będzie dziedziczył z obydwu, tj. Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method, która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do właściwości value, która jest jednyną instancją Foo i stała się jego prototypem. -Ważne jest, aby pamiętać new Bar nie tworzy nowej instancji Foo, -ale wykorzystuje instancje, którą jest przypisana do własności prototype. +Należy pamiętać, że new Bar nie tworzy nowej instancji Foo, +tylko wykorzystuje instancję, która jest przypisana do własności prototype. Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.

Wyszukiwanie własności

-

Podczas dostępu do właściwości obiektu, JavaScript przejdzie w górę łańcucha -prototypów dopóki nie znajdzie właściwości z żądaną nazwą.

+

Podczas dostępu do właściwości obiektu JavaScript przejdzie w górę łańcucha +prototypów, dopóki nie znajdzie właściwości bez nazwy.

-

Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha mianowicie Object.prototype +

Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha, mianowicie Object.prototype i nadal nie znajdzie określonej właściwości, to zwróci wartość undefined.

@@ -214,42 +215,42 @@

Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu -do nieistniejącej właściwości powoduje zawsze przeszukanie całego łańcucha prototypów.

+do nieistniejącej właściwości zawsze spowoduje przeszukanie całego łańcucha prototypów.

-

Również, podczas iteracji po właściwościach obiektu -każda właściwość, która znajduje się w łańcuchu prototypów niezależnie -na jakim znajduje się poziomie zostanie wyliczona.

+

Również podczas iteracji po właściwościach obiektu +każda właściwość, która znajduje się w łańcuchu prototypów (niezależnie +na jakim znajduje się poziomie) zostanie wyliczona.

Rozszerzanie natywnych prototypów

Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z -najczęściej używanych niedoskonałej częsci języka JavaScript.

+najczęściej nadużywanej częsci języka JavaScript.

Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. -Jednak jest szeroko rozpowszechniona w frameworkach takich jak Prototype. -Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez dodawanie do nich -niestandardowych funkcjonalności.

+Mimo to jest szeroko rozpowszechniona w frameworkach takich jak Prototype. +Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez wzbogacanie ich o +niestandardowe funkcjonalności.

Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie
-funkcjonalności znajdujących sie w nowszych silnikach JavaScript np. Array.forEach

+funkcjonalności znajdujących sie w nowszych silnikach JavaScript, np. Array.forEach

Wnioski

-

Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczanie -należy całkowicie rozumieć prototypowy model dziedziczenia. Ponadto należy uważać -na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń -aby uniknąć problemów z wydajnością. Natywne prototypy nie powinny nigdy być +

Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczania,
+należy całkowicie zrozumieć prototypowy model dziedziczenia. Ponadto trzeba uważać +na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń, +aby uniknąć problemów z wydajnością. Natywne prototypy nigdy nie powinny być rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami -JavaScript.

hasOwnProperty

W celu sprawdzenia czy dana właściwość została zdefiniowana w tym obiekcie a nie -w łańcuchu prototypów niezbędne jest skorzystanie z metody -hasOwnProperty, która wszystkie obiekty dziedziczą z Object.prototype.

+JavaScript.

hasOwnProperty

W celu sprawdzenia, czy dana właściwość została zdefiniowana w tym obiekcie, a nie +w łańcuchu prototypów, niezbędne jest skorzystanie z metody +hasOwnProperty, której wszystkie obiekty dziedziczą z Object.prototype.

-

hasOwnProperty jest jedyna metodą w języku JavaScript która operuje na właściwościach +

hasOwnProperty jest jedyną metodą w języku JavaScript, która operuje na właściwościach i nie przegląda całego łańcucha prototypów.

// Zatrucie Object.prototype
@@ -271,7 +272,7 @@
 

hasOwnProperty jako właściwość

JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje -możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne jest użycie +możliwość, że obiekt będzie posiadać tak nazwaną właściwość. Konieczne jest użycie zewnętrznego hasOwnProperty, aby otrzymać poprawne rezultaty.

var foo = {
@@ -290,16 +291,16 @@
 
 

Wnioski

-

Jedyną metodą służącą do sprawdzenia zdefiniowania jakiejś właściwości w konkretnym -obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty jako część -każdej pętli for in, pozwoli to uniknąć błędów pochodzących z -rozszerzonych natywnych prototypów.

The for in Loop

Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów +

Jedyną metodą służącą do sprawdzenia istnienia jakiejś właściwości w konkretnym +obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty w +każdej pętli for in. Pozwoli to uniknąć błędów pochodzących +z rozszerzonych natywnych prototypów.

Pętla for in

Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów podczas iteracji po właściwościach obiektu.

// Zatrucie Object.prototype
@@ -311,17 +312,17 @@
 }
 
-

Ponieważ nie jest możliwe, aby zmienić zachowanie pętli for in to niezbędne +

Ponieważ zmiana zachowania pętli for in nie jest możliwa, niezbędne jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając z metody hasOwnProperty z Object.prototype.

-

Korzystanie z hasOwnProperty do odfiltrowania

+

Filtrowania przy użyciu hasOwnProperty

// foo z przykładu powyżej
 for(var i in foo) {
@@ -331,20 +332,20 @@
 }
 
-

To jest jedyna poprawna wersja, którą należy używać. Ze względu na użycie -hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty -kod będzie podatny na błędy, gdy natywne prototypy np. Object.prototype -zostanie rozszerzony.

+

To jest jedyna poprawna wersja, której należy używać. Ze względu na użycie +hasOwnProperty zostanie wypisane jedynie moo. Gdy opuścimy hasOwnProperty, +kod będzie podatny na błędy, gdy natywne prototypy (np. Object.prototype) +zostaną rozszerzone.

-

Prototype jest jednym z szeroko rozpowszechniony frameworków, który dokonuje -takiego rozszerzenia. Używanie tego frameworku oraz nie używanie w pętle for in +

Prototype jest jednym z popularniejszych frameworków, które dokonują +takiego rozszerzenia. Używanie tego frameworku oraz nie stosowanie w pętli for in metody hasOwnProperty gwarantuje błędy w wykonaniu.

Wnioski

-

Zaleca się aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać -żadnych założeń na temat środowiska, w którym kod będzie wykonywany i czy natywne -prototypy zostały rozszerzone czy nie.

Funkcje

Deklaracje funkcji i wyrażenia funkcyjne

Funcje w języku JavaScript są typami pierwszoklasowymi. Co oznacza, że mogą +

Zaleca się, aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać +żadnych założeń na temat środowiska, w którym kod będzie wykonywany ani tego, czy +natywne prototypy zostały rozszerzone, czy nie.

Funkcje

Deklaracje funkcji i wyrażenia funkcyjne

Funcje w języku JavaScript są typami pierwszoklasowymi, co oznacza, że mogą być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie asynchronicznej funkcji.

@@ -354,9 +355,9 @@
function foo() {}
 
-

Powyższa funkcja zostaje wyniesiona zanim program wystartuje, dzięki temu +

Powyższa funkcja zostaje wyniesiona zanim program wystartuje. Dzięki temu jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana, -nawet jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

+nawet, jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.

foo(); // Działa ponieważ definicja funkcji została wyniesiona 
        // na początek zasięgu przed uruchomieniem kodu
@@ -375,7 +376,7 @@
 var foo = function() {};
 
-

Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu, +

Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt będzie wykonywany.

@@ -383,7 +384,7 @@ ustawiona na domyślną wartość undefined zanim powyższy kod zostanie uruchomiony.

-

Nazwane wyrażenia funkdyjne

+

Nazwane wyrażenia funkcyjne

Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.

@@ -393,13 +394,13 @@ bar(); // wyrzuca ReferenceError
-

W zewnętrznym zakresie bar nie będzie dostępne, ponieważ funkcja zostaje +

W zewnętrznym zakresie bar nie będzie dostępna, ponieważ funkcja zostaje przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna. Jest to spowodowane tym, jak działa rozwiązywanie nazw w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym -zakresie tej funkcji.

Jak działa this

JavaScript posiada inną koncepcję odnośnie tego na co wskazuje specjalna -nazwa this, niż większość innych języków programowania. Istnieją dokładnie -pięć różnych sytuacji w których wartość this zostaje przypisana w języku JavaScript.

+zakresie tej funkcji.

Jak działa this

JavaScript posiada inną koncepcję odnośnie tego na co wskazuje słowo kluczowe +this, niż większość innych języków programowania. Istnieje dokładnie +pięć różnych sytuacji, w których wartość this jest przypisana w języku JavaScript.

JavaScript has a different concept of what the special name this refers to than most other programming languages do. There are exactly five different @@ -410,7 +411,7 @@

this;
 
-

Używanie this w globalnym zasięgu, zwróci po prostu referencje do obiektu global.

+

Używanie this w globalnym zasięgu, zwróci po prostu referencję do obiektu global.

Wywołanie funkcji

@@ -436,7 +437,7 @@
new foo(); 
 
-

Wywołanie funkcji, które jest poprzedzone słowem kluczowym new zachowuje się +

Wywołanie funkcji, które jest poprzedzone słowem kluczowym new, zachowuje się jak konstruktor. Wewnątrz funkcji this będzie wskazywało na nowo utworzony obiekt.

@@ -476,10 +477,10 @@ }
-

Powszechnym nieporozumieniem jest, że this wewnątrz test wskazuje na Foo, +

Powszechnym błędem jest myślenie, że this wewnątrz test wskazuje na Foo, podczas gdy w rzeczywistości tak nie jest.

-

Aby uzyskać dostęp do Foo wewnątrz test niezbędne jest stworzenie wewnątrz +

Aby uzyskać dostęp do Foo wewnątrz test, niezbędne jest stworzenie wewnątrz metody lokalnej zmiennej, która będzie wskazywała na Foo.

Foo.method = function() {
@@ -491,13 +492,13 @@
 }
 
-

that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać -wartość zewnętrznego this. W połączeniu z domknięciami(closures) -jest to sposób na przekazywanie wartości this wokoło.

+

that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja otrzymywania
+wartości zewnętrznego this. W połączeniu z domknięciami(closures), +jest to sposób na przekazywanie wartości this wokół.

Metody przypisywania

-

Kolejną rzeczą, która nie działa w języku JavaScript jest nadawanie aliasów +

Kolejną rzeczą, która nie działa w języku JavaScript, jest nadawanie aliasów funkcjom, co oznacza przypisanie metody do zmiennej.

var test = someObject.methodTest;
@@ -508,7 +509,7 @@
 funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.

Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem, -to w rzeczywistości jest to rzecz, która powoduje że +to w rzeczywistości jest to rzecz, która sprawia, że dziedziczenie prototypowe działa.

function Foo() {}
@@ -521,8 +522,8 @@
 

Kiedy metoda method zostanie wywołana na instancji Bar, this będzie -wskazywało właśnie tą instancję.

Domknięcia i referencje

Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia, -oznacza to że zasięg zawsze posiada dostęp do zewnętrznego zasięgu w którym +wskazywało właśnie tę instancję.

Domknięcia i referencje

Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia. +Oznacza 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 funckję, wszystkie funkcje domyślnie zachowują się jak domknięcia.

@@ -546,16 +547,16 @@ foo.get(); // 5 -

Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funckję get. -Obie te funkcję trzymają referencję do zasięgu Counter a co za tym idzie +

Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funkcję get. +Obie te funkcje trzymają referencję do zasięgu Counter, a co za tym idzie zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana w zasięgu tych funkcji.

-

Dlaczego zmienne przywatne działają

+

Dlaczego zmienne prywatne działają?

-

Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, to -nie istnieje sposób aby uzyskać dostęp do zmiennej count z zewnątrz. -Wykorzystanie tych dwóch domkinęć jest jedynym sposobem na interakcję z tą zmienną.

+

Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript, +nie istnieje sposób, aby uzyskać dostęp do zmiennej count z zewnątrz. +Wykorzystanie tych dwóch domknięć jest jedynym sposobem na interakcję z tą zmienną.

var foo = new Counter(4);
 foo.hack = function() {
@@ -570,7 +571,7 @@
 

Domknięcia wewnątrz pętli

Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli, -aby wartość zmiennej po której odbywa się iteracja był kopiowana do +aby wartość zmiennej po której odbywa się iteracja była kopiowana do wewnętrznej funkcji.

for(var i = 0; i < 10; i++) {
@@ -604,8 +605,8 @@
 }
 
-

Zewnętrzna anonimowa funkcja zostaje wywołana od razu z parametrem i -jako pierwszym argumentem i otrzyma kopię wartości zmiennej i jako +

Zewnętrzna anonimowa funkcja zostanie wywołana od razu z parametrem i +jako pierwszym argumentem oraz otrzyma kopię wartości zmiennej i jako zmienną e.

Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada @@ -622,21 +623,21 @@ } })(i), 1000) } -

Obiekt arguments

Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. +

Obiekt arguments

Każdy zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments. Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.

-

Obiekt arguments nie jest typu Array. Mimo, że posiada pewne cechy -semantyki tablic - właściwość length - to nie dziedziczy on z Array.prototype, -ale w rzeczywistości z Object.

+

Obiekt arguments nie jest typu Array. Mimo że posiada pewne cechy +semantyki tablic - właściwość length - to w rzeczywistości nie dziedziczy +on z Array.prototype, tylko z Object.

-

Ze względu na to nie można używać standardowych dla tablic metod takich jak -push, pop czy slice na obiekcie arguments. Mimo, że iteracja przy pomocy +

Ze względu na to, na obiekcie arguments nie można używać standardowych dla tablic metod, +takich jak push, pop czy slice. Mimo że iteracja przy pomocy pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych należy skonwertować arguments do prawdziwego obiekt Array.

@@ -654,7 +655,7 @@

Przekazywanie argumentów

Zalecany sposób przekazywania argumentów z jednej funkcji do następnej -wyglada następująco.

+wyglada następująco:

function foo() {
     bar.apply(null, arguments);
@@ -684,11 +685,11 @@
 
 

Parametry formalne i indeksy argumentów

-

Obiekt arguments tworzy funckje getter i setter nie tylko dla swoich +

Obiekt arguments tworzy funkcje getter i setter nie tylko dla swoich właściwości, ale również dla parametrów formalnych funkcji.

W rezultacie zmiana wartości parametru formalnego zmieni również wartość -odpowiadającemu mu wpisowi w obiekcie arguments, zachodzi to również w drugą stronę.

+odpowiadającemu mu wpisowi w obiekcie arguments. Zachodzi to również w drugą stronę.

function foo(a, b, c) {
     arguments[0] = 2;
@@ -706,7 +707,7 @@
 
 

Mity i prawdy o wydajności

-

Obiekt arguments jest zawsze tworzony z wyjątkiem dwóch przypadków, gdy +

Obiekt arguments jest tworzony zawsze, z wyjątkiem dwóch przypadków, gdy zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest używany czy nie.

@@ -738,9 +739,8 @@

W powyższym przykładzie foo nie może zostać wykorzystana metoda inline ponieważ potrzebne są nie tylko informacje na własny temat ale również na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia -inlining i korzyści z niej wynikające, ale również stanowi złamanie -zasad enkapsulacji ponieważ ta funkcja jest zależna od kontekstu -w jakim została wywołana.

+inlining i korzyści z niego wynikające, ale też łamie zasady enkapsulacji, +ponieważ ta funkcja jest zależna od kontekstu w jakim została wywołana.

Mocno zalecane jest aby nigdy nie korzystać z arguments.callee i żadnej jej własności.

@@ -748,8 +748,8 @@

Konstruktory

Konstruktory w JavaScript również wyglądają inaczej niż innych języka. Każde -wywołanie funkcji, które jest poprzedone słowem kluczowym new zachowuje się +

Konstruktory

Konstruktory w JavaScript również wyglądają inaczej niż innych językach. Każde +wywołanie funkcji, które jest poprzedone słowem kluczowym new, zachowuje się jak konstruktor.

Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na @@ -793,7 +793,7 @@ new Test(); // zwrócony obiekt -

Jeżeli słowo kluczowe new zostanie pominięte funkcja nie zwróci nowego +

Jeżeli słowo kluczowe new zostanie pominięte, funkcja nie zwróci nowego obiektu.

function Foo() {
@@ -802,13 +802,13 @@
 Foo(); // undefined
 
-

Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku -z działaniem this w języku JavaScript to jako +

Mimo że powyższy kod może zadziałać w pewnych przypadkach, w związku +z działaniem this w języku JavaScript, to jako wartość thiszostanie wykorzystany obiekt global.

Fabryki

-

Aby móc ominąć słowo kluczowe new konstruktor musi jawnie zwracać wartość.

+

Aby móc ominąć słowo kluczowe new, konstruktor musi jawnie zwracać wartość.

function Bar() {
     var value = 1;
@@ -826,13 +826,13 @@
 Bar();
 
-

Oba wywołania Bar zwrócą tą samą rzecz, nowo utworzony obiekt, który posiada -właściwość nazwaną method w sobie, dla którego Bar jest Domknięciem.

+

Oba wywołania Bar zwrócą tę samą rzecz, nowo utworzony obiekt, który posiada +właściwość nazwaną method i dla którego Bar jest Domknięciem.

Należy również pamiętać, że wywołanie new Bar() nie ma wpływu na prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype). -Podczas gdy prototyp zostanie przypisany do nowo utworzonego obiektu, to jednak Bar -nidgy nie zwróci tego nowego obiektu Bar, ale literał obiektu, który jest po +Kiedy prototyp zostanie przypisany do nowo utworzonego obiektu, Bar nidgy +nie zwróci tego nowego obiektu Bar, tylko literał obiektu, który jest po słowie kluczowym return.

W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem @@ -840,8 +840,8 @@

Tworzenie nowych obiektów korzystając z fabryk

-

Często zaleca się nie korzystać z operatora new ponieważ zapominając -go zastosować może prowadzić do błędów.

+

Często zaleca się nie korzystać z operatora new, ponieważ zapominanie +o jego stosowaniu może prowadzić do błędów.

W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować nowy obiekt wewnątrz tej fabryki.

@@ -862,14 +862,14 @@ }
-

Mimo, że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia +

Mimo że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia korzystanie ze zmiennych prywatnych, to posiada pewne wady. While the above is robust against a missing new keyword and certainly makes the use of private variables easier, it comes with some downsides. 1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod - poprzez prototyp + poprzez prototyp. 2. Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp do nowo utworzonego obiektu. @@ -881,7 +881,7 @@

Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie -ważne jest aby wybrać określony styl tworzenia obiektów i się go trzymać.

Zasięg zmiennych i przestrzenie nazw

Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące +ważne jest, aby wybrać określony styl tworzenia obiektów i trzymać się go.

Zasięg zmiennych i przestrzenie nazw

Mimo że JavaScript radzi sobie dobrze ze składnią opisującą dwa pasujące nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego. Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.

@@ -895,18 +895,17 @@

W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest definiowane w jednej globalnie współdzielonej przestrzeni nazw.

-

Za każdym razem gdy zmienna jest -Z każdym odwołaniem do zmiennej JavaScript przeszukuje w górę wszystkie zasięgi -dopóki nie znajdzie tej zmiennej. W przypadku gdy przeszukiwanie dotrze do globalnego -zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd ReferenceError.

+

Z każdym odwołaniem do zmiennej, JavaScript przeszukuje w górę wszystkie zasięgi +dopóki nie znajdzie tej zmiennej. W przypadku, gdy przeszukiwanie dotrze do globalnego +zasięgu i nadal nie znajdzie żądanej nazwy, to wyrzuca błąd ReferenceError.

Zmora globalnych zmiennych

@@ -917,11 +916,11 @@ var foo = '42' -

Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienna -nazwaną foo w globalnym zasięgu natomiast skrypt B definiuje foo +

Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienną +nazwaną foo w globalnym zasięgu, natomiast skrypt B definiuje foo w aktualnym zasięgu.

-

Jeszcze raz, to wcale nie daje tego samego efektu, nie użycie var może mieć +

Jeszcze raz, to wcale nie daje tego samego efektu. Nie użycie var może mieć poważne konsekwencje.

// globalny zasięg
@@ -935,8 +934,8 @@
 

Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość -zmiennej globalnej foo. Mimo, że nie wygląda to na początku jak duży problem, -to posiadając wiele tysięcy linii kodu w JavaScript i nie korzystanie z var +zmiennej globalnej foo. Mimo że nie wygląda to na początku na duży problem, +posiadanie wielu tysięcy linii kodu w JavaScript i nie korzystanie z var wprowadzi straszne i trudne do wyśledzenia błędy.

// globalny zasięg 
@@ -955,7 +954,7 @@
 
 

Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli -for pozwoliło by łatwo uniknąć problemu. Słowo kluczowe var nie powinno być +for pozwoliłoby łatwo uniknąć problemu. Słowo kluczowe var nie powinno być nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na zewnętrzny zasięg.

@@ -1034,15 +1033,15 @@ test();
-

Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętle, +

Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętli, ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

-

W oryginalnym kodzie wewnątrz deklaracja if zdaje się modyfikować zmienną +

W oryginalnym kodzie instrukcja warunkowa if zdaje się modyfikować zmienną globalną goo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym jak zostało zastosowane windowanie (hoisting).

-

Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może wydawać się -wyrzucać błąd ReferenceError.

+

Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może sprawiać wrażenie, +że zobaczymy błąd ReferenceError.

// sprawdz czy SomeImportantThing zostało zainicjalizowane
 if (!SomeImportantThing) {
@@ -1057,7 +1056,7 @@
 
 // inny kod który może ale nie musi zainicjalizować SomeImportantThing
 
-// upewnienie sie że SomeImportantThing zostało zainicjalizowane
+// upewnienie sie, że SomeImportantThing zostało zainicjalizowane
 if (!SomeImportantThing) {
     SomeImportantThing = {};
 }
@@ -1066,7 +1065,7 @@
 

Kolejność rozwiązywania nazw

Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają -zdefiniowana wewnątrz specjalną nazwę this, która wskazuje +zdefiniowaną wewnątrz specjalną nazwę this, która wskazuje na aktualny obiekt.

Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę @@ -1081,8 +1080,8 @@ 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.

Przestrzenie nazw

@@ -1101,17 +1100,17 @@ })(); // natychmiastowe wykonanie funkcji -

Nienazwane funkcje są rozpoznane jako wyrażenia, więc +

Anonimowe funkcje są rozpoznane jako wyrażenia, więc aby mogły zostać wywołane muszą zostać zewaluowane.

-
( // zewaluowanie fukcji znajdującej się wewnątrz nawiasów
+
( // zewaluowanie funkcji znajdującej się wewnątrz nawiasów
 function() {}
 ) // zwrócenie obiektu funkcji
 () // wywołanie rezultatu ewaluacji
 
-

Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo, że -mają inną składnie, zachowują się dokładnie tak samo.

+

Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo że +mają inną składnię, zachowują się dokładnie tak samo.

// Trzy inne sposoby
 !function(){}();
@@ -1121,15 +1120,15 @@
 
 

Wnioski

-

Zaleca się aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz +

Zaleca się, aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale również wprowadza lepszą modularyzację programów.

Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę. -Jakiekolwiek wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, -który jest podatny na błędy i trudny do utrzymania.

Tablice

Iterowanie po tablicach oraz właściwości tablic

Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania -pętli for in do iteracji po nich. W rzeczywiści istnieje -wiele dobrych powodów przeciwko wykorzystania for in na tablicach.

+Wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, który +jest podatny na błędy i trudny do utrzymania.

Tablice

Iterowanie po tablicach oraz właściwościach tablic

Mimo że tablice w JavaScript są obiektami, nie ma dobrych powodów aby używać +pętli for in do iteracji po nich. W rzeczywstości istnieje +wiele dobrych powodów przeciwko wykorzystaniu for in na tablicach.

Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz -łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości to użycie +łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości jest użycie hasOwnProperty, ale wówczas pętla staje się dwadzieście razy wolniejsza od normalnej pętli for.

@@ -1153,22 +1152,22 @@ }
-

Jest tam jeszcze jeden dodatkowy haczyk w przykładzie powyżej. Jest to zbuforowanie +

W powyższym przykładzie jest jeszcze jeden dodatkowy haczyk. Jest to zbuforowanie długości tablicy poprzez l = list.length.

-

Mimo, że właściwość length jest zdefiniowana w wewnątrz tablicy, istnieje nadal +

Mimo że właściwość length jest zdefiniowana wewnątrz tablicy, istnieje nadal dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli. -Chociaż najnowsze silniki JavaScript mogą zastosować optymalizację w tym -przypadku. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym -z tych nowych silników czy też nie.

+Chociaż najnowsze silniki JavaScript mogą zastosować w tym +przypadku optymalizację. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym +z tych nowych silników, czy też nie.

-

W rzeczywistości pomijając buforowanie długości tablicy może spowodować, że pętla +

W rzeczywistości pominięcie buforowania długości tablicy może spowodować, że pętla będzie tylko w połowie tak szybka jak ta z buforowaniem długości.

Właściwość length

-

Mimo, że getter właściwości length po prostu zwraca liczbę elementów, które są -zawarte w tablicy, to setter może być użyta do skracania tablicy.

+

Mimo, że getter właściwości length zwraca po prostu liczbę elementów, które są +zawarte w tablicy, to setter może być użyty do skracania tablicy.

var foo = [1, 2, 3, 4, 5, 6];
 foo.length = 3;
@@ -1185,9 +1184,9 @@
 
 

Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest -znakiem źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

Konstruktor Array

Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia +oznaką źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.

Konstruktor Array

Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje -parametry do niego przekazane.

+przekazane do niego parametry.

[1, 2, 3]; // Rezultat: [1, 2, 3]
 new Array(1, 2, 3); // Rezultat: [1, 2, 3]
@@ -1200,16 +1199,16 @@
 

W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę z ustawioną właściwością length na wartość przekazaną jako argument. Należy -zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób, -rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

+zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób. +Rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.

var arr = new Array(3);
 arr[1]; // undefined
 1 in arr; // zwraca false, indeks nie został ustawiony
 
-

Możliwość ustanienia z góry długości tablicy jest użyteczna tylko w kilku -przypadkach, jak powtarzanie ciągu znaków, w którym unika się stosowania +

Możliwość ustalenia z góry długości tablicy jest użyteczna tylko w kilku +przypadkach, jak np. powtarzanie ciągu znaków, w którym unika się stosowania pętli for.

// count - ilosc powtorzen
@@ -1219,8 +1218,8 @@
 
 

Wnioski

-

W miare możliwości należy unikać używania konstruktora Array. Literały są -zdecydowanie lepszym rozwiązaniem, są krótsze i mają bardziej precyzyjną składnię. +

W miarę możliwości należy unikać używania konstruktora Array. Literały są +zdecydowanie lepszym rozwiązaniem. Są krótsze i mają bardziej precyzyjną składnię. Zwiększają również czytelność kodu.

Typy

Równość i porównania

JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.

Operator równości

@@ -1243,16 +1242,16 @@

Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą -praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędy.

+praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędów.

-

Ponadto koercja ma również wpływ na wydajność na przykład gdy typ String musi zostać +

Ponadto koercja ma również wpływ na wydajność, Na przykład gdy typ String musi zostać przekształcony na typ Number przed porównaniem z drugą liczbą.

Operator ścisłej równości

Operator ścisłej równości składa się z trzech znaków "równa się": ===

-

Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem nie +

Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem - nie dokonuje koercji typów przed porównaniem.

""           ===   "0"           // false
@@ -1272,8 +1271,8 @@
 
 

Porównywanie obiektów

-

Mimo, że oba operatory == i === nazywane są operatorami równościowymi, -to zachowują się różnie gdy jednym z operandów jest obiekt typu Object.

+

Mimo że oba operatory == i === nazywane są operatorami równościowymi, +to zachowują się różnie, gdy jednym z operandów jest obiekt typu Object.

{} === {};                   // false
 new String('foo') === 'foo'; // false
@@ -1283,19 +1282,19 @@
 

Oba operatory porównują toższmość a nie równość, czyli będą porównywać czy -jeden i drugi operand jest tą samą instancją obiektu, podobnie jak operator -is w Pythonie i porównanie wskaźników w C.

+jeden i drugi operand jest tą samą instancją obiektu (podobnie jak operator +is w Pythonie i porównanie wskaźników w C).

Wnioski

-

Zaleca się aby używać tylko operatora ścisłej równości. W sytuacjach gdy +

Zaleca się, aby używać tylko operatora ścisłej równości. W sytuacjach gdy potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna -być dokonana jawnie a nie pozostawiona trudnym regułom koercji +być dokonana jawnie, a nie pozostawiona trudnym regułom koercji obowiązującym w języku.

Operator typeof

Operator typeof (razem z operatorem instanceof) jest -prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript, jest on praktycznie -całkowicie wadliwy.

+prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript. Posiada on praktycznie
+same wady.

-

Mimo, że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, +

Mimo że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce, natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek nie jest związany z sprawdzaniem typu obiektu.

@@ -1383,9 +1382,9 @@ implementacjach.

O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została -zdefiniowana, powinien być unikany o ile to tylko możliwe.

Operator instanceof

Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. -Jest on jedynie użyteczny do porównywania obiektów utworzonych klas. Stosowanie -go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora +zdefiniowana, powinien być unikany jeśli to tylko możliwe.

Operator instanceof

Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy. +Jest on użyteczny jedynie do porównywania obiektów utworzonych klas. Stosowanie +go na wbudowanych typach jest praktycznie tak samo bezużyteczne, jak operatora typeof.

Porównywanie obiektów utworzonych klas

@@ -1397,7 +1396,7 @@ new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true -// Poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo +// poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo // a nie faktyczną instancję Foo Bar.prototype = Foo; new Bar() instanceof Foo; // false @@ -1419,51 +1418,51 @@

Wnioski

-

Operator instanceof powinien być tylko używany podczas korzystania z obiektów +

Operator instanceof powinien być używany wyłącznie podczas korzystania z obiektów klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym. Podobnie jak operator typeof, należy unikać korzystania -z tego operatora w innych sytuacjach.

Rzutowanie typów

JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +z tego operatora w innych sytuacjach.

Rzutowanie typów

JavaScript jest językiem słabo typowanym. Co za tym idzie, będzie stosować koercję typów gdziekolwiek jest to możliwe.

-
// These are true
+
// te zwracają true
 new Number(10) == 10; // Number.toString() zostanie przekształcone
                       // z powrotem do liczby
 
-10 == '10';           // Stringi zostaną przekształcone do typu Number
-10 == '+10 ';         // Kolejne wariacje
+10 == '10';           // stringi zostaną przekształcone do typu Number
+10 == '+10 ';         // kolejne wariacje
 10 == '010';          // i następne
 isNaN(null) == false; // null zostanie przekształcony do 0
                       // który oczywiście nie jest NaN
 
-// Poniższe zwracają false
+// poniższe zwracają false
 10 == 010;
 10 == '-10';
 
-

Aby uniknąć powyższych problemów, należy koniecznie skorzystać ze +

Aby uniknąć powyższych problemów, należy koniecznie korzystać ze ściełego operatora równości. Mimo, że pozwala to uniknąć wiele typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego typowania języka JavaScript.

Konstruktory typów wbudowanych

-

Konstruktory typów wbudowanych takich, jak Number lub String zachowują się -inaczej jeżeli są poprzedzone słowem kluczowym new a inaczej jeżeli nie są.

+

Konstruktory typów wbudowanych, takich jak Number lub String, zachowują się +inaczej kiedy są poprzedzone słowem kluczowym new a inaczej kiedy nie są.

new Number(10) === 10;     // False, Object i Number
 Number(10) === 10;         // True, Number i Number
 new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
 
-

Korzystanie z wbudowanych typów jak Number jako konstruktor utworzy nowy obiekt -typu Number, natomiast opuszczenie słowa kluczowego new spowoduje, że funkcja -Number zachowa się jak konwerter.

+

Korzystanie z wbudowanych typów jak Number jako konstruktora tworzy nowy obiekt +typu Number, natomiast opuszczenie słowa kluczowego new powoduje, że funkcja +Number zachowuje się jak konwerter.

Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą ilością rzutowań (koercją) typów.

@@ -1486,7 +1485,7 @@

Rzutowanie do typu Boolean

-

Używając dwukrotnie operatora negacji dowolna wartość może zostać zrzutowana +

Używając dwukrotnie operatora negacji, dowolna wartość może zostać zrzutowana do typu Boolean

!!'foo';   // true
@@ -1496,7 +1495,7 @@
 !!'-1'     // true
 !!{};      // true
 !!true;    // true
-

Jądro

Dlaczego nie należy używać eval

Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

+

Jądro

Dlaczego nie należy używać eval?

Funkcja eval uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie).

var foo = 1;
 function test() {
@@ -1508,8 +1507,8 @@
 foo; // 1
 
-

Niestaty eval zostanie wykonana w lokalnym zasięgu tylko jeżeli została wywołana -bezpośrednio i nazwa wołanej funkcji równa sie eval.

+

Niestaty, eval zostanie wykonana w lokalnym zasięgu tylko wtedy, gdy zostanie wywołana +bezpośrednio i nazwa wywoływanej funkcji równa sie eval.

var foo = 1;
 function test() {
@@ -1528,72 +1527,70 @@
 

eval w przebraniu

Funkcje wykonywane po upływie czasu setTimeout i setInterval -mogą przyjąć string jako pierwszy argument. String ten zostanie zawsze wykonany -w globalnym zasięgu, ponieważ funkcja eval zostanie wywołana niebezpośrednio w tym -przypadku.

+mogą przyjąć string jako pierwszy argument. String ten zawsze będzie wykonywany +w globalnym zasięgu, ponieważ funkcja eval jest w tym wypadku wywoływana pośrednio.

Problemy z bezpieczeństwem

Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ -wykonuje każdy kod, który zostanie do niej przekazany i nie należy nigdy -używać jej na stringach nieznanego lub niezaufanego pochodzenia.

+wykonuje każdy kod, który zostanie do niej przekazany i nigdy nie należy +jej używać na stringach nieznanego lub niezaufanego pochodzenia.

Wnioski

-

Funkcja eval nie powinna być w ogole używana, każdy kod, który ją wykorzystuje +

Funkcja eval nie powinna być w ogóle używana. Każdy kod, który jej używa powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa. W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod -należy przemyśleć raz jeszcze i ulepszyć kod aby nie wymagał użycia eval.

undefined i null

JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną +należy ponownie przemyśleć i ulepszyć aby nie wymagał użycia eval.

undefined i null

JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną z tych dwóch jest undefined.

Wartość undefined

undefined jest typem z dokładnie jedną wartością: undefined.

-

Język również definiuje globalną zmienną, która ma wartość undefined, zmienna -ta jest nazwana undefined. Jednakże jest to zmienna a nie stała czy słowo -kluczowe w języku. Oznacza to że możliwe jest nadpisanie wartości tej zmiennej.

+

Język również definiuje globalną zmienną, która ma wartość undefined - zmienna +ta jest nazwana undefined. Jednakże jest to zmienna a nie stała, czy słowo +kluczowe. Oznacza to, że możliwe jest nadpisanie wartości tej zmiennej.

Kilka przykładów kiedy wartość undefined jest zwracana:

    -
  • Dostęp do (niemodyfikowalnej) zmiennej globalnej undefined.
  • -
  • Wyjście z funkcji, która nie ma deklaracji return.
  • -
  • Deklaracja return, która nic jawnie nie zwraca.
  • -
  • Poszukiwanie nieistniejącej właściwości.
  • -
  • Parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji
  • -
  • Wszystko co zostało ustawione na wartość undefined
  • +
  • dostęp do (niemodyfikowalnej) zmiennej globalnej undefined,
  • +
  • wyjście z funkcji, która nie ma deklaracji return,
  • +
  • deklaracja return, która nic jawnie nie zwraca,
  • +
  • poszukiwanie nieistniejącej właściwości,
  • +
  • parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji,
  • +
  • wszystko czemu została przypisana wartość undefined.

Obsługa przypadku zmiany wartości undefined

-

Ponieważ globalna zmienna undeined tylko zawiera kopię prawdziwej wartości typu +

Ponieważ globalna zmienna undefined zawiera tylko kopię prawdziwej wartości typu undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości typu undefined.

-

Jednak, aby porównać coś do wartości undefined potrzebne jest odczytanie wartości -undefined.

+

Jednak aby porównać coś z wartością undefined, trzeba odczytać wartość undefined.

-

Aby uchronić swój kod przeciwko możliwemu nadpisaniu zmiennej undefined, korzysta +

Aby uchronić swój kod przed możliwym nadpisaniem zmiennej undefined, korzysta się z powszechnej techniki dodania dodatkowego parametru do anonimowego wrappera, do którego nie zostanie przekazany argument.

var undefined = 123;
 (function(something, foo, undefined) {
-    // undefined lokalnym zasięgu znowu 
+    // undefined o lokalnym zasięgu znowu 
     // odnosi się do poprawnej wartości
 
 })('Hello World', 42);
 
-

Kolejnym sposobem aby osiągnąć ten sam efekt jest użycie deklaracji zmiennej +

Kolejnym sposobem na osiągnięcie tego samego efektu jest użycie deklaracji zmiennej wewnątrz wrappera.

var undefined = 123;
@@ -1604,21 +1601,21 @@
 })('Hello World', 42);
 
-

Jedyną różnicą pomięcy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo +

Jedyną różnicą pomiędzy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo kluczowe var i spację po nim.

Zastosowanie null

Podczas gdy undefined w kontekście języka jest używany jak null w sensie -tradycyjnych języków, to null w JavaScript (jako literał i jako typ) jest po +tradycyjnych języków, null w JavaScript (jako literał i jako typ) jest po prostu kolejnym typem danych.

Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go -zastąpić przez undefined.

Automatyczne wstawianie średnika

Mimo, że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania +zastąpić przez undefined.

Automatyczne wstawianie średnika

Mimo że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.

-

Lecz JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje +

JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript automatycznie wstawia średniki o ile napotka błąd parsowania związany z brakiem średnika.

@@ -1636,7 +1633,7 @@

Automatyczne wstawianie średników jest uważane za jeden z największych błędów -konstrukcji języka, ponieważ może ono zachowanie kodu.

+konstrukcji języka, ponieważ może ono zmienić zachowanie kodu.

Jak działa wstawianie

@@ -1704,12 +1701,12 @@ -

Parser drastycznie zmienił działanie powyższego kodu, w niektórych przypadkach +

Parser drastycznie zmienił działanie powyższego kodu. W niektórych przypadkach zmienił go źle.

Nawiasy

@@ -1721,23 +1718,23 @@ (options.list || []).forEach(function(i) {}) -

Ten kod zostanie zmieniony w poniższą jedną linię.

+

Kod ten zostanie zmieniony w poniższą linię.

log('testing!')(options.list || []).forEach(function(i) {})
 
-

Jest bardzo prawdopodobne, że log nie zwróci fukcji, co za tym idzie +

Jest bardzo prawdopodobne, że log nie zwróci fukcji. Co za tym idzie powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a function - undefined nie jest funkcją.

Wnioski

-

Zaleca się aby nigdy nie pomijać średników, pozostawiać nawias otwierający +

Zaleca się, aby nigdy nie pomijać średników, pozostawiać nawias otwierający w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji -if / else bez nawiasów nawet jeżeli są jednolinijkowe. Wszystkie te uwagi nie -tylko pomagają poprawić spójność kodu, ale również zapobiegają parser JavaScript -przed zmianą działania kod.

Inne

setTimeout i setInterval

Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania -funkcji korzystając z funkcji setTimeout i setInterval. +if / else bez nawiasów - nawet, jeżeli są jednolinijkowe. Wszystkie te uwagi nie +tylko pomagają poprawić spójność kodu, ale też zapobiegają zmianie działania +kodu przez parser JavaScript.

Inne

setTimeout i setInterval

Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji przy użyciu funkcji setTimeout i setInterval. Since JavaScript is asynchronous, it is possible to schedule the execution of a function by using the setTimeout and setInterval functions.

@@ -1747,7 +1744,7 @@
function foo() {}
-var id = setTimeout(foo, 1000); // zwraca licznę typu Number > 0
+var id = setTimeout(foo, 1000); // zwraca liczbę typu Number > 0
 

Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za @@ -1757,11 +1754,11 @@ upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego, że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko -jedno wątkowy.

+jednowątkowy.

Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji -będzie wkazywać na obiekt global.

+będzie wskazywać na obiekt global.

function Foo() {
     this.value = 42;
@@ -1776,23 +1773,23 @@
 
 
 
 

Kolejkowanie wywołań z setInterval

-

Podczas, gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - +

Podczas gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval - jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X milisekund. Jednakże korzystanie z tej funkcji jest odradzane.

Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji, -setInterval będzie próbować uruchamiać daną funkcję co będzie powodować -kolejkowanie wykonania tej samej funkcji kilkakrotnie. W szczególności może się -to wydarzyć przy krótkim interwale.

+setInterval będzie próbować uruchamiać daną funkcję, co będzie powodować +kolejkowanie wykonania tej samej funkcji kilkukrotnie. Może się to zdażyć +szczególnie przy krótkim interwale.

function foo(){
     // coś co blokuje wykonanie na 1 sekundę 
@@ -1803,13 +1800,13 @@
 

W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na jedną sekundę.

-

Podczas, gdy funkcja foo blokuje wykonanie setInterval będzie planować kolejne -wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, już -w kolejce do wywołania będą czekały kolejne dziesięć wywołań tej funkcji.

+

Podczas, gdy funkcja foo blokuje wykonanie, setInterval będzie planować kolejne +wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy, +w kolejce do wywołania będzie już czekało kolejne dziesięć wywołań tej funkcji.

Radzenie sobie z możliwymi blokadami

-

Najprostrzym jak również najbardziej kontrolowaną sytuacją jest użycie setTimeout +

Najprostszą, jak również najbardziej kontrolowaną sytuacją, jest użycie setTimeout wewnątrz wywoływanej funkcji.

function foo(){
@@ -1819,9 +1816,9 @@
 foo();
 
-

Powyższy kod nie tylko hermetyzuje wywołanie setTimeout ale również zapobiega -kolejkowaniu wywołań fukcji i daje dodatkową kontrolę. W tym przypadku funkcja -foo może zdecydować czy powinna się wywołać ponownie czy też nie.

+

Powyższy kod nie tylko hermetyzuje wywołanie setTimeout, ale też zapobiega +kolejkowaniu wywołań funkcji i daje dodatkową kontrolę. W tym przypadku funkcja +foo może zdecydować czy powinna się wywołać ponownie, czy też nie.

Ręczne usuwanie budzików

@@ -1835,8 +1832,8 @@

Usuwanie wszystkich budzików

-

Ponieważ nie istnieje wbudowana metoda na usunięcie wszystkich budzików i/lub -interwałów, konieczne jest użycie metody brute force aby osiągnąć ten efekt.

+

Ponieważ nie istnieje wbudowana metoda usuwania wszystkich budzików i/lub +interwałów, do osiągnięcia tego efektu konieczne jest użycie metody 'brute force'.

// usunięcie "wszystkich" budzików 
 for(var i = 1; i < 1000; i++) {
@@ -1844,9 +1841,9 @@
 }
 
-

Nadal może istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała, -ponieważ ID było z innego przedziału, dlatego zamiast korzystania z metody brute -force, zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

+

Nadal mogą istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała. +Ponieważ ID było z innego przedziału, zamiast korzystania z metody brute force, +zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.

Ukryte wykorzystanie eval

@@ -1874,11 +1871,11 @@ bar(); -

Ponieważ eval nie zostało wywołane wprost w tym przypadku, to -string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym, -co za tym idzie lokalna zmienna foo z zasięgu bar nie zostanie użyta.

+

Ponieważ eval nie zostało wywołane w tym przypadku wprost, to +string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym. +Co za tym idzie, lokalna zmienna foo z zasięgu bar nie zostanie użyta.

-

Kolejnym zaleceniem jest aby nie stosować stringów do przekazywania argumentów +

Kolejnym zaleceniem jest niestosowanie stringów do przekazywania argumentów do funkcji, która ma zostać wywołana przez budzik.

function foo(a, b, c) {}
@@ -1886,26 +1883,26 @@
 // NIGDY nie należy tak robić 
 setTimeout('foo(1,2, 3)', 1000)
 
-// Zamiast tego należy skorzystać z anonimowej funkcji
+// zamiast tego należy skorzystać z anonimowej funkcji
 setTimeout(function() {
     foo(a, b, c);
 }, 1000)
 

Wnioski

-

Nie należy nigdy przekazywać stringu jako parametru do setTimeout lub -setInterval. Jest to wyraźną oznaką bardzo złego kodu, jeżeli potrzebne jest -przekazanie argumentów do funkcji należy skorzystać z anonimowej funkcji i +

Nigdy nie należy przekazywać stringu jako parametru do setTimeout lub +setInterval. Jest to wyraźną oznaką bardzo złego kodu. Jeżeli potrzebne jest +przekazanie argumentów do funkcji, należy skorzystać z anonimowej funkcji i wewnątrz niej dokonać przekazania argumentów.

-

Ponadto, należy unikać korzystanie z setInterval, ponieważ planista może +

Ponadto, należy unikać korzystania z setInterval, ponieważ planista może zablokować wykonanie JavaScriptu.