From 9f506be95f7f5fb9160daca3caccec4a83e708e8 Mon Sep 17 00:00:00 2001 From: Dmitry Scriptin Date: Sun, 27 Mar 2011 00:37:00 +0300 Subject: [PATCH 001/469] added empty article about delete operator (EN) --- doc/en/core/delete.md | 0 doc/en/index.json | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 doc/en/core/delete.md diff --git a/doc/en/core/delete.md b/doc/en/core/delete.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/en/index.json b/doc/en/index.json index d4e52883..0c078397 100644 --- a/doc/en/index.json +++ b/doc/en/index.json @@ -59,7 +59,8 @@ "articles": [ "eval", "undefined", - "semicolon" + "semicolon", + "delete" ] }, { From bd0f12a7e39d45d314454e3fcbb23213b09c0735 Mon Sep 17 00:00:00 2001 From: Dmitry Scriptin Date: Sun, 27 Mar 2011 03:02:29 +0400 Subject: [PATCH 002/469] added content to operator article --- doc/en/core/delete.md | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/doc/en/core/delete.md b/doc/en/core/delete.md index e69de29b..37a12816 100644 --- a/doc/en/core/delete.md +++ b/doc/en/core/delete.md @@ -0,0 +1,87 @@ +## The `delete` Operator + +In short, it's *impossible* to delete global variables, functions and some other +stuff in JavaScript which have a `DontDelete` attribute set. + +### Global code and Function code + +When a variable or a function is defined in a global +or a [function scope](#function.scopes) it is a property of either +Activation object or Global object. Such properties have a set of attributes, +one of these is `DontDelete`. Variable and function declarations in global +and function code always create properties with `DontDelete`, therefore +cannot be deleted. + + // global variable: + var a = 1; // DontDelete is set + delete a; // false + a; // 1 + + // normal function: + function f() {} // DontDelete is set + delete f; // false + typeof f; // "function" + + // reassigning doesn't help: + f = 1; + delete f; // false + f; // 1 + +### Explicit properties + +There are things which can be deleted normally: these are explicitly set +properties. + + // explicitly set property: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +In the example above `obj.x` and `obj.y` can be deleted because they have no +`DontDelete` atribute. That's why an example below works too. + + // this works fine, except for IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - just a global var + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Here we use a trick to delete `a`. [`this`](#function.this) here refers +to the Global object and we explicitly declare variable `a` as it's property +which allows us to delete it. + +IE (at least 6-8) has some bugs, so code above doesn't work. + +### Function arguments and built-ins + +Functions' normal arguments, [`arguments` object](#function.arguments) +and built-in properties also have `DontDelete` set. + + // function arguments and properties: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### Host objects + +Behaviour of `delete` operator can be unpredictable for hosted objects. Due to +specification, host objects are allowed to implement any kind of behavior. + +### In conclusion + +`delete` operator often has an unexpected behaviour and can be safely used +only for dealing with explicitly set properties on normal objects. From b089bc821bf7ce94e3b5a6435028d2c30a50ec0d Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:11:24 +0300 Subject: [PATCH 003/469] Merged intro. --- doc/tr/index.json | 6 +----- doc/tr/intro/index.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/doc/tr/index.json b/doc/tr/index.json index 36a94b8d..e6654186 100644 --- a/doc/tr/index.json +++ b/doc/tr/index.json @@ -6,11 +6,7 @@ { "title": "Giriş", "dir": "intro", - "articles": [ - "authors", - "contributors", - "license" - ] + "articles": [] }, { "title": "Nesneler", diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index 303ef891..f747fd0f 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -11,5 +11,38 @@ anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız istiyorsanız, lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. +## Yazarlar + +Bu rehber, sevimli birer [Stack Overflow][2] kullanıcısı olan [Ivo Wetzel][3] (Yazım) +ve [Zhang Yi Jiang][4] (Tasarım) tarafından hazırlanmıştır. + +## Katkıda Bulunanlar + + - [Caio Romão][5] (Yazım düzeltmeleri) + - [Andreas Blixt][6] (Dil düzeltmeleri) + +## Hosting +JavaScript Garden'a GitHub üzerinden, ve ayrıca [Cramer Development][7] +tarafından desteklenen [JavaScriptGarden.info][8] adresinden ulaşılabilir. + +## Lisans + +JavaScript Garden [MIT lisansı][9] altında yayınlanmıştır ve [GitHub][10] +üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına +rastlarsanız lütfen [sorunu bize bildirin][11] veya bir `pull request` gönderin. +Bizi ayrıca Stack Overflow'da [JavaScript sohbet odasında][12] da +bulabilirsiniz. + [1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript From 95397b5e192643d6971b7e8fe0e8bef312b9d31a Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:11:32 +0300 Subject: [PATCH 004/469] Merged intro. --- doc/tr/intro/authors.md | 8 -------- doc/tr/intro/contributors.md | 8 -------- doc/tr/intro/license.md | 13 ------------- 3 files changed, 29 deletions(-) delete mode 100644 doc/tr/intro/authors.md delete mode 100644 doc/tr/intro/contributors.md delete mode 100644 doc/tr/intro/license.md diff --git a/doc/tr/intro/authors.md b/doc/tr/intro/authors.md deleted file mode 100644 index 6e53d523..00000000 --- a/doc/tr/intro/authors.md +++ /dev/null @@ -1,8 +0,0 @@ -## Yazarlar - -Bu rehber, sevimli birer Stack Overflow kullanıcısı olan [Ivo Wetzel][1] (Yazım) -ve [Zhang Yi Jiang][2] (Tasarım) tarafından hazırlanmıştır. - -[1]: http://stackoverflow.com/users/170224/ivo-wetzel -[2]: http://stackoverflow.com/users/313758/yi-jiang - diff --git a/doc/tr/intro/contributors.md b/doc/tr/intro/contributors.md deleted file mode 100644 index ba517abc..00000000 --- a/doc/tr/intro/contributors.md +++ /dev/null @@ -1,8 +0,0 @@ -## Katkıda Bulunanlar - - - [Caio Romão][1] (Yazım düzeltmeleri) - - [Andreas Blixt][2] (Dil düzeltmeleri) - -[1]: https://github.com/caio -[2]: https://github.com/blixt - diff --git a/doc/tr/intro/license.md b/doc/tr/intro/license.md deleted file mode 100644 index a0484d74..00000000 --- a/doc/tr/intro/license.md +++ /dev/null @@ -1,13 +0,0 @@ -## Lisans - -JavaScript Garden [MIT lisansı][1] altında yayınlanmıştır ve [GitHub][2] -üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına -rastlarsanız lütfen [sorunu bildirin][3] veya bir `pull request` gönderin. -Bizi ayrıca Stack Overflow'da [JavaScript sohbet kanalında][4] da -bulabilirsiniz. - -[1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE -[2]: https://github.com/BonsaiDen/JavaScript-Garden -[3]: https://github.com/BonsaiDen/JavaScript-Garden/issues -[4]: http://chat.stackoverflow.com/rooms/17/javascript - From 69ab28a6fe8542bdca48c6d5fb113b3154749998 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:16:24 +0300 Subject: [PATCH 005/469] Fixes #56 in Turkish translation. --- doc/tr/object/hasownproperty.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/tr/object/hasownproperty.md b/doc/tr/object/hasownproperty.md index e5eb5df0..634b3695 100644 --- a/doc/tr/object/hasownproperty.md +++ b/doc/tr/object/hasownproperty.md @@ -42,8 +42,9 @@ doğru sonuç alabilmek için `hasOwnProperty `*haricen* kullanılmalıdır. foo.hasOwnProperty('bar'); // her zaman false verir - // hasOwnProperty haricen kullanıldığında 'this' foo olur - {}.hasOwnProperty.call(foo, 'bar'); // true + // hasOwnProperty başka bir nesne üzerinde + // kullanıldığında 'this' foo olur + ({}).hasOwnProperty.call(foo, 'bar'); // true ### Sonuç From 806c9a6ffe8b45e587fea2bce9e34ed9a1b69c56 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:21:29 +0300 Subject: [PATCH 006/469] Same as BonsaiDen/JavaScript-Garden@c9e3df32127b663bcbacb9b8cf1eda2d4aa791e5 for Turkish translation. --- doc/tr/function/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tr/function/general.md b/doc/tr/function/general.md index e45949c1..84d54801 100644 --- a/doc/tr/function/general.md +++ b/doc/tr/function/general.md @@ -29,8 +29,8 @@ Bu örnekte *isimsiz fonksiyon* `foo` değişkenine atanır. Yukarıdaki `var` anahtar kelimesi bir bildirim olduğu için `foo` değişkeni program çalışmadan önce yukarı alınır, program çalıştığında `foo` tanımlanmştır. -Fakat değer ataması program çalışırken gerçekleşeceği için, ilgili satır -çalıştığında, `foo` değişkeninin değeri varsayılan olarak +Fakat değer atamaları sadece program çalışırken gerçekleşeceği için, ilgili +satır çalıştığında, `foo` değişkeninin değeri varsayılan olarak [undefined](#core.undefined) olacaktır. ### İsimli fonksiyon ifadesi From 2c35f13507c92a6365dedb0bed273d4a4ff95206 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:23:36 +0300 Subject: [PATCH 007/469] Same as BonsaiDen/JavaScript-Garden@07f23c9de9a6245dd3d2986bf285d00a7072fdc8 for Turkish translation. --- doc/tr/object/general.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md index b112a582..7cc81d7c 100644 --- a/doc/tr/object/general.md +++ b/doc/tr/object/general.md @@ -61,8 +61,8 @@ açabilecek özellik isimlerinin kullanılmasına izin vermesidir. ### Özellikleri silmek Bir nesnenin özelliklerinden birini silmenin tek yolu `delete` operatörünü -kullanmaktır; özelliğe `undefined` veya `null` değerlerini atamak **sadece** -özelliğin değerini kaldırır, anahtarı değil. +kullanmaktır; özelliğe `undefined` veya `null` değerlerini atamak sadece +özelliğin *değerini* kaldırır, *anahtarı* değil. var obj = { bar: 1, From cc5b30f9251e52c80a69667840f14dbacf886e50 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 01:28:45 +0300 Subject: [PATCH 008/469] Same as BonsaiDen/JavaScript-Garden@1b3a48104122202bb02ca451b83c2e3abd8aeaf2 for Turkish translation. --- doc/tr/other/timeouts.md | 7 +++---- doc/tr/types/equality.md | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/tr/other/timeouts.md b/doc/tr/other/timeouts.md index 594ba83c..5454b8e8 100644 --- a/doc/tr/other/timeouts.md +++ b/doc/tr/other/timeouts.md @@ -111,8 +111,8 @@ olarak `eval` kullanılır. > **Not:** Zamanlama fonksiyonları ECMAScript Standartında bulunmadığı için, > bir katar argümanı almaları durumundaki çalışma şekilleri JavaScript motorları -> arasında farklılık gösterebilir. Gerçekten de, Microsoft'un JScript motoru -> `eval` yerine `Function` oluşturucusunu kullanır. +> arasında farklılık gösterebilir. Mesela, Microsoft'un JScript motoru `eval` +> yerine `Function` oluşturucusunu kullanır. function foo() { // setTimeOut ile bu fonksiyon çağrılacaktır @@ -157,5 +157,4 @@ bulunduran bir *isimsiz fonksiyon* kullanılmalıdır. Ayrıca, `setInterval` fonksiyonu çalışan JavaScript programı tarafından bloke olmadığı için tercih edilmemelidir. -[1]: http://en.wikipedia.org/wiki/Document_Object_Model - +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/doc/tr/types/equality.md b/doc/tr/types/equality.md index a0d5d296..3240a54e 100644 --- a/doc/tr/types/equality.md +++ b/doc/tr/types/equality.md @@ -3,11 +3,11 @@ JavaScript'de nesnelerin değerlerinin eşitliğini kontrol etmenin iki farklı yolu vardır. -### Eşittir operatörü +### Eşitlik operatörü -Eşittir operatörü iki adet eşittir işaretinden oluşur: `==` +Eşitlik operatörü iki adet eşittir işaretinden oluşur: `==` -JavaScript *weakly typed* bir dildir, bu nedenle, eşittir operatörü ile +JavaScript *weakly typed* bir dildir. Bu nedenle, eşitlik operatörü ile değişkenleri karşılaştırırken **tip dönüşümü** yapar. "" == "0" // false From 192a1df798d436ec5a7b245f410d14941ec5c73a Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 26 Apr 2011 02:07:00 +0300 Subject: [PATCH 009/469] Translated hosting subsection. --- doc/tr/intro/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index f747fd0f..e6f1fce6 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -21,9 +21,10 @@ ve [Zhang Yi Jiang][4] (Tasarım) tarafından hazırlanmıştır. - [Caio Romão][5] (Yazım düzeltmeleri) - [Andreas Blixt][6] (Dil düzeltmeleri) -## Hosting -JavaScript Garden'a GitHub üzerinden, ve ayrıca [Cramer Development][7] -tarafından desteklenen [JavaScriptGarden.info][8] adresinden ulaşılabilir. +## Sunum + +JavaScript Garden GitHub üzerinden, ve ayrıca [Cramer Development][7] +tarafından desteklenen [JavaScriptGarden.info][8] adresinden sunulmaktadır. ## Lisans From 53decceb1391f2e68b38a95aaf144a5d6f7c1dba Mon Sep 17 00:00:00 2001 From: MONTILLET Xavier Date: Tue, 26 Apr 2011 11:33:00 -0700 Subject: [PATCH 010/469] Added another way to clear "all" timeouts and replaced the "is is" by "it is". --- doc/en/other/timeouts.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/en/other/timeouts.md b/doc/en/other/timeouts.md index d196a3b7..02d39ea6 100644 --- a/doc/en/other/timeouts.md +++ b/doc/en/other/timeouts.md @@ -94,8 +94,14 @@ it is necessary to use brute force in order to achieve this functionality. clearTimeout(i); } -There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so +But there might still be timeouts that are unaffected by this arbitrary number. Another way of doing this is to consider that the ID given to a timeout is incremented by one everytime you call `setTimeout`. + + // clear "all" timeouts + for(var i = 1; i <= window.setTimeout(function(){}, 1); i++) { + clearTimeout(i); + } + +But even though this works on all main browsers nowadays, it isn't specified that the IDs should be ordered that way and it may change. Therefore, it is instead recommended to keep track of all the timeout IDs, so they can be cleared specifically. ### Hidden use of `eval` From 0a894c199ac7e9f77e6f3fb8463b9b2d5cd111b4 Mon Sep 17 00:00:00 2001 From: MONTILLET Xavier Date: Tue, 26 Apr 2011 14:12:47 -0700 Subject: [PATCH 011/469] Stored the biggest timeout ID to avoid infinite loop. --- doc/en/other/timeouts.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/en/other/timeouts.md b/doc/en/other/timeouts.md index 02d39ea6..655fcc35 100644 --- a/doc/en/other/timeouts.md +++ b/doc/en/other/timeouts.md @@ -97,7 +97,9 @@ it is necessary to use brute force in order to achieve this functionality. But there might still be timeouts that are unaffected by this arbitrary number. Another way of doing this is to consider that the ID given to a timeout is incremented by one everytime you call `setTimeout`. // clear "all" timeouts - for(var i = 1; i <= window.setTimeout(function(){}, 1); i++) { + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { clearTimeout(i); } From e531742c68b989e31fbe7f31ffd313c387c212d1 Mon Sep 17 00:00:00 2001 From: Adeel Ahmad Khan Date: Thu, 5 May 2011 18:59:41 -0700 Subject: [PATCH 012/469] minor --- doc/ru/intro/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ru/intro/index.md b/doc/ru/intro/index.md index fa7c6636..9debe341 100644 --- a/doc/ru/intro/index.md +++ b/doc/ru/intro/index.md @@ -1,6 +1,6 @@ ## Вступление -**JavaScript Гарден** это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. +**JavaScript Гарден** — это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. From 5ef7ea516b63e405895f45f2e97f101a3a200901 Mon Sep 17 00:00:00 2001 From: oozcitak Date: Tue, 17 May 2011 01:17:16 +0300 Subject: [PATCH 013/469] Fixed a little typo. --- doc/tr/intro/index.md | 7 +++---- doc/tr/object/general.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/tr/intro/index.md b/doc/tr/intro/index.md index e6f1fce6..5c39210a 100644 --- a/doc/tr/intro/index.md +++ b/doc/tr/intro/index.md @@ -2,14 +2,13 @@ **JavaScript Garden** JavaScript programlama dilinin acayiplikleri üzerine derlenmiş bir döküman koleksiyonudur. Henüz ustalaşmamış JavaScript -programcılarının sıkça yaptığı yanlışlar, dile has ince hatalar ve performans +programcılarının sıkça yaptığı yanlışlar, dile has incelikler ve performans sorunlarına karşı tavsiyeler içerir. JavaScript Garden'ın amacı size JavaScript öğretmek **değildir**. Bu rehberde anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız -şiddetle tavsiye edilir. Eğer JavaScript dilinin temellerini öğrenmek -istiyorsanız, lütfen Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] -başvurun. +gerekir. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, lütfen +Mozilla Programcı Ağı'nda bulunan mükemmel [rehbere][1] başvurun. ## Yazarlar diff --git a/doc/tr/object/general.md b/doc/tr/object/general.md index 7cc81d7c..6107ca66 100644 --- a/doc/tr/object/general.md +++ b/doc/tr/object/general.md @@ -21,7 +21,7 @@ Bu hatayı aşıp sayı sabitlerinin de nesne olarak davranmasını sağlamak i uygulanabilecek bazı çözümler vardır. 2..toString(); // ikinci nokta doğru şekilde algılanır - 2 .toString(); // noktanın solundki boşluğa dikkat edin + 2 .toString(); // noktanın solundaki boşluğa dikkat edin (2).toString(); // ilk önce 2 değerlendirilir ### Bir veri türü olarak nesneler From 9d166aa72e08dc1cdeba8f5866c61ca976652597 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 21:49:10 +0900 Subject: [PATCH 014/469] cp en --- doc/ja/array/constructor.md | 35 +++++ doc/ja/array/general.md | 58 ++++++++ doc/ja/core/eval.md | 48 +++++++ doc/ja/core/semicolon.md | 114 ++++++++++++++++ doc/ja/core/undefined.md | 72 ++++++++++ doc/ja/function/arguments.md | 119 ++++++++++++++++ doc/ja/function/closures.md | 98 ++++++++++++++ doc/ja/function/constructors.md | 128 ++++++++++++++++++ doc/ja/function/general.md | 48 +++++++ doc/ja/function/scopes.md | 231 ++++++++++++++++++++++++++++++++ doc/ja/function/this.md | 111 +++++++++++++++ doc/ja/index.json | 68 ++++++++++ doc/ja/intro/index.md | 47 +++++++ doc/ja/object/forinloop.md | 51 +++++++ doc/ja/object/general.md | 99 ++++++++++++++ doc/ja/object/hasownproperty.md | 53 ++++++++ doc/ja/object/prototype.md | 116 ++++++++++++++++ doc/ja/other/timeouts.md | 155 +++++++++++++++++++++ doc/ja/types/casting.md | 70 ++++++++++ doc/ja/types/equality.md | 71 ++++++++++ doc/ja/types/instanceof.md | 38 ++++++ doc/ja/types/typeof.md | 87 ++++++++++++ 22 files changed, 1917 insertions(+) create mode 100644 doc/ja/array/constructor.md create mode 100644 doc/ja/array/general.md create mode 100644 doc/ja/core/eval.md create mode 100644 doc/ja/core/semicolon.md create mode 100644 doc/ja/core/undefined.md create mode 100644 doc/ja/function/arguments.md create mode 100644 doc/ja/function/closures.md create mode 100644 doc/ja/function/constructors.md create mode 100644 doc/ja/function/general.md create mode 100644 doc/ja/function/scopes.md create mode 100644 doc/ja/function/this.md create mode 100644 doc/ja/index.json create mode 100644 doc/ja/intro/index.md create mode 100644 doc/ja/object/forinloop.md create mode 100644 doc/ja/object/general.md create mode 100644 doc/ja/object/hasownproperty.md create mode 100644 doc/ja/object/prototype.md create mode 100644 doc/ja/other/timeouts.md create mode 100644 doc/ja/types/casting.md create mode 100644 doc/ja/types/equality.md create mode 100644 doc/ja/types/instanceof.md create mode 100644 doc/ja/types/typeof.md diff --git a/doc/ja/array/constructor.md b/doc/ja/array/constructor.md new file mode 100644 index 00000000..763f55fb --- /dev/null +++ b/doc/ja/array/constructor.md @@ -0,0 +1,35 @@ +## The `Array` Constructor + +Since the `Array` constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - `[]` notation - +when creating new arrays. + + [1, 2, 3]; // Result: [1, 2, 3] + new Array(1, 2, 3); // Result: [1, 2, 3] + + [3]; // Result: [3] + new Array(3); // Result: [] + new Array('3') // Result: ['3'] + +In cases when there is only one argument passed to the `Array` constructor, +and that argument is a `Number`, the constructor will return a new *sparse* +array with the `length` property set to the value of the argument. It should be +noted that **only** the `length` property of the new array will be set this way, +the actual indexes of the array will not be initialized. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, the index was not set + +The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +`for loop` code. + + new Array(count + 1).join(stringToRepeat); + +### In Conclusion + +The use of the `Array` constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code. + diff --git a/doc/ja/array/general.md b/doc/ja/array/general.md new file mode 100644 index 00000000..7c8f07ab --- /dev/null +++ b/doc/ja/array/general.md @@ -0,0 +1,58 @@ +## Array Iteration and Properties + +Although arrays in JavaScript are objects, there are no good reasons to use +the [`for in loop`](#object.forinloop) in for iteration on them. In fact there +are a number of good reasons **against** the use of `for in` on arrays. + +> **Note:** JavaScript arrays are **not** *associative arrays*. JavaScript only +> has [objects](#object.general) for mapping keys to values. And while associative +> arrays **preserve** order, objects **do not**. + +Since the `for in` loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +[`hasOwnProperty`](#object.hasownproperty), it is already up to **twenty times** +slower than a normal `for` loop. + +### Iteration + +In order to achieve the best performance when iterating over arrays, it is best +to use the classic `for` loop. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +There is one extra catch in the above example, that is the caching of the +length of the array via `l = list.length`. + +Although the `length` property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines **may** apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not. + +In fact, leaving out the caching may result in the loop being only **half as +fast** as with the cached length. + +### The `length` Property + +While the *getter* of the `length` property simply returns the number of +elements that are contained in the array, the *setter* can be used to +**truncate** the array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array. + +### In Conclusion + +For the best performance it is recommended to always use the plain `for` loop +and cache the `length` property. The use of `for in` on an array is a sign of +badly written code that is prone to bugs and bad performance. + diff --git a/doc/ja/core/eval.md b/doc/ja/core/eval.md new file mode 100644 index 00000000..ecf7ba90 --- /dev/null +++ b/doc/ja/core/eval.md @@ -0,0 +1,48 @@ +## Why Not to Use `eval` + +The `eval` function will execute a string of JavaScript code in the local scope. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +But `eval` only executes in local scope when it is being called **directly** *and* +the name of the called function is actually `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +The use of `eval` should be avoided at **all costs**. 99.9% of its "uses" can be +achieved **without** it. + +### `eval` in Disguise + +The [timeout functions](#other.timeouts) `setTimeout` and `setInterval` can both +take a string as their first argument. This string will **always** get executed +in the global scope since `eval` is not being called directly in that case. + +### Security Issues + +`eval` also is a security problem as it executes **any** code given to it, +it should **never** be used with strings of unknown or untrusted origins. + +### In Conclusion + +`eval` should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires `eval` in +order to work, its design is to be questioned and should **not** be used in the +first place, a *better design* should be used, that does not require the use of +`eval`. + diff --git a/doc/ja/core/semicolon.md b/doc/ja/core/semicolon.md new file mode 100644 index 00000000..61d74fd9 --- /dev/null +++ b/doc/ja/core/semicolon.md @@ -0,0 +1,114 @@ +## Automatic Semicolon Insertion + +Although JavaScript has C style syntax, it does **not** enforce the use of +semicolons in the source code, it is possible to omit them. + +But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser **automatically** inserts them whenever it encounters a parse +error due to a missing semicolon. + + var foo = function() { + } // parse error, semicolon expected + test() + +Insertion happens, and the parser tries again. + + var foo = function() { + }; // no error, parser continues + test() + +The automatic insertion of semicolon is considered to be one of **biggest** +design flaws in the language, as it *can* change the behavior of code. + +### How it Works + +The code below has no semicolons in it, so it is up to the parser to decide where +to insert them. + + (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) + +Below is the result of the parser's "guessing" game. + + (function(window, undefined) { + function test(options) { + + // Not inserted, lines got merged + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inserted + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inserted + + return; // <- inserted, breaks the return statement + { // treated as a block + + // a label and a single expression statement + foo: function() {} + }; // <- inserted + } + window.test = test; // <- inserted + + // The lines got merged again + })(window)(function(window) { + window.someLibrary = {}; // <- inserted + + })(window); //<- inserted + +> **Note:** The JavaScript parser does not "correctly" handle return statements +> which are followed by a new line, while this is not neccessarily the fault of +> the automatic semicolon insertion, it can still be an unwanted side-effect. + +The parser drastically changed the behavior of the code above, in certain cases +it does the **wrong thing**. + +### Leading Parenthesis + +In case of a leading parenthesis, the parser will **not** insert a semicolon. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +This code gets transformed into one line. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Chances are **very** high that `log` does **not** return a function; therefore, +the above will yield a `TypeError` stating that `undefined is not a function`. + +### In Conclusion + +It is highly recommended to **never** omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line `if` / `else` statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior. + diff --git a/doc/ja/core/undefined.md b/doc/ja/core/undefined.md new file mode 100644 index 00000000..b29da3b4 --- /dev/null +++ b/doc/ja/core/undefined.md @@ -0,0 +1,72 @@ +## `undefined` and `null` + +JavaScript has two distinct values for `nothing`, the more useful of these two +being `undefined`. + +### The Value `undefined` + +`undefined` is a type with exactly one value: `undefined`. + +The language also defines a global variable that has the value of `undefined`, +this variable is also called `undefined`. But this variable is **not** a constant, +nor is it a keyword of the language. This means that its *value* can be easily +overwritten. + +> **ES5 Note:** `undefined` in ECMAScript 5 is **no longer** *writable* in strict +> mode, but its name can still be shadowed by for example a function with the name +> `undefined`. + +Some examples for when the value `undefined` is returned: + + - Accessing the (unmodified) global variable `undefined`. + - Implicit returns of functions due to missing `return` statements. + - `return` statements which do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters which do not had any explicit value passed. + - Anything that has been set to the value of `undefined`. + +### Handling Changes to the Value of `undefined` + +Since the global variable `undefined` only holds a copy of the actual *value* of +`undefined`, assigning a new value to it does **not** change the value of the +*type* `undefined`. + +Still, in order to compare something against the value of `undefined` it is +necessary to retrieve the value of `undefined` first. + +In order to protect code against a possible overwritten `undefined` variable, a +common technique used is to add an additional parameter to an +[anonymous wrapper](#function.scopes), that gets no argument passed to it. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined in the local scope does + // now again refer to the value + + })('Hello World', 42); + +Another way to achieve the same effect would be to use a declaration inside the +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other `var` statement inside the +anonymous wrapper. + +### Uses of `null` + +While `undefined` in the context of the JavaScript language is mostly used in +the sense of a traditional *null*, the actual `null` (both a literal and a type) +is more or less just another data type. + +It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting `Foo.prototype = null`), but in almost all cases it +can be replaced by `undefined`. + + diff --git a/doc/ja/function/arguments.md b/doc/ja/function/arguments.md new file mode 100644 index 00000000..3d551ad5 --- /dev/null +++ b/doc/ja/function/arguments.md @@ -0,0 +1,119 @@ +## The `arguments` Object + +Every function scope in JavaScript can access the special variable `arguments`. +This variable holds a list of all the arguments that were passed to the function. + +> **Note:** In case `arguments` has already been defined inside the function's +> scope either via a `var` statement or being the name of a formal parameter, +> the `arguments` object will not be created. + +The `arguments` object is **not** an `Array`. While it has some of the +semantics of an array - namely the `length` property - it does not inherit from +`Array.prototype` and is in fact an `Object`. + +Due to this, it is **not** possible to use standard array methods like `push`, +`pop` or `slice` on `arguments`. While iteration with a plain `for` loop works +just fine, it is necessary to convert it to a real `Array` in order to use the +standard `Array` methods on it. + +### Converting to an Array + +The code below will return a new `Array` containing all the elements of the +`arguments` object. + + Array.prototype.slice.call(arguments); + +This conversion is **slow**, it is **not recommended** to use it in performance +critical sections of code. + +### Passing Arguments + +The following is the recommended way of passing arguments from one function to +another. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Another trick is to use both `call` and `apply` together to create fast, unbound +wrappers. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // It takes the parameters: this, arg1, arg2...argN + Foo.method = function() { + + // Result: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Formal Parameters and Arguments Indices + +The `arguments` object creates *getter* and *setter* functions for both its +properties as well as the function's formal parameters. + +As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the `arguments` object, and the other way around. + + 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); + +### Performance Myths and Truths + +The `arguments` object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not. + +Both *getters* and *setters* are **always** created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the `arguments` object's properties. + +> **ES5 Note:** These *getters* and *setters* are not created in strict mode. + +However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of `arguments.callee`. + + function foo() { + arguments.callee; // do something with this function object + arguments.callee.caller; // and the calling function object + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Would normally be inlined... + } + } + +In the above code, `foo` can no longer be a subject to [inlining][1] since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context. + +It is **highly recommended** to **never** make use of `arguments.callee` or any of +its properties. + +> **ES5 Note:** In strict mode, `arguments.callee` will throw a `TypeError` since +> its use has been deprecated. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/doc/ja/function/closures.md b/doc/ja/function/closures.md new file mode 100644 index 00000000..17554dcb --- /dev/null +++ b/doc/ja/function/closures.md @@ -0,0 +1,98 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*, +this means that scopes **always** keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +[function scope](#function.scopes), all functions, by default, act as closures. + +### Emulating private variables + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Here, `Counter` returns **two** closures. The function `increment` as well as +the function `get`. Both of these functions keep a **reference** to the scope of +`Counter` and, therefore, always keep access to the `count` variable that was +defined in that very scope. + +### Why Private Variables Work + +Since it is not possible to reference or assign scopes in JavaScript, there is +**no** way of accessing the variable `count` from the outside. The only way to +interact with it is via the two closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +The above code will **not** change the variable `count` in the scope of `Counter`, +since `foo.hack` was not defined in **that** scope. It will instead create - or +override - the *global* variable `count`. + +### Closures Inside Loops + +One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +The above will **not** output the numbers `0` through `9`, but will simply print +the number `10` ten times. + +The *anonymous* function keeps a **reference** to `i` and at the time +`console.log` gets called, the `for loop` has already finished and the value of +`i` as been set to `10`. + +In order to get the desired behavior, it is necessary to create a **copy** of +the value of `i`. + +### Avoiding the Reference Problem + +In order to copy the value of the loop's index variable, it is best to use an +[anonymous wrapper](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +The anonymous outer function gets called immediately with `i` as its first +argument and will receive a copy of the **value** of `i` as its parameter `e`. + +The anonymous function that gets passed to `setTimeout` now has a reference to +`e`, whose value does **not** get changed by the loop. + +There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/doc/ja/function/constructors.md b/doc/ja/function/constructors.md new file mode 100644 index 00000000..ad90b028 --- /dev/null +++ b/doc/ja/function/constructors.md @@ -0,0 +1,128 @@ +## Constructors + +Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the `new` keyword acts as a constructor. + +Inside the constructor - the called function - the value of `this` refers to a +newly created `Object`. The [`prototype`](#object.prototype) of this **new** +object is set to the `prototype` of the function object that was invoked as the +constructor. + +If the function that was called has no explicit `return` statement, then it +implicitly returns the value of `this` - the new object. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +The above calls `Foo` as constructor and sets the `prototype` of the newly +created object to `Foo.prototype`. + +In case of an explicit `return` statement the function returns the value +specified that statement, **but only** if the return value is an `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Foo() { + this.bla = 1; // gets set on the global object + } + Foo(); // undefined + +While the above example might still appear to work in some cases, due to the +workings of [`this`](#function.this) in JavaScript, it will use the +*global object* as the value of `this`. + +### Factories + +In order to be able to omit the `new` keyword, the constructor function has to +explicitly return a value. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Both calls to `Bar` return the exact same thing, a newly create object which +has a property called `method` on it, that is a +[Closure](#function.closures). + +It is also to note that the call `new Bar()` does **not** affect the prototype +of the returned object. While the prototype will be set on the newly created +object, `Bar` never returns that new object. + +In the above example, there is no functional difference between using and +not using the `new` keyword. + + +### Creating New Objects via Factories + +An often made recommendation is to **not** use `new` since forgetting its use +may lead to bugs. + +In order to create new object, one should rather use a factory and construct a +new object inside of that factory. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +While the above is robust against a missing `new` keyword and certainly makes +the use of [private variables](#function.closures) easier, it comes with some +downsides. + + 1. It uses more memory since the created objects do **not** share the methods + on a prototype. + 2. In order to inherit the factory needs to copy all the methods from another + object or put that object on the prototype of the new object. + 3. Dropping the prototype chain just because of a left out `new` keyword + somehow goes against the spirit of the language. + +### In Conclusion + +While omitting the `new` keyword might lead to bugs, it is certainly **not** a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation **and stick** +with it. + diff --git a/doc/ja/function/general.md b/doc/ja/function/general.md new file mode 100644 index 00000000..64c9e0c9 --- /dev/null +++ b/doc/ja/function/general.md @@ -0,0 +1,48 @@ +## Function Declarations and Expressions + +Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an *anonymous function* as a callback to another, possibly asynchronous function. + +### The `function` Declaration + + function foo() {} + +The above function gets [hoisted](#function.scopes) before the execution of the +program starts; thus, it is available *everywhere* in the scope it was *defined* +in, even if called before the actual definition in the source. + + foo(); // Works because foo was created before this code runs + function foo() {} + +### The `function` Expression + + var foo = function() {}; + +This example assigns the unnamed and *anonymous* function to the variable `foo`. + + foo; // 'undefined' + foo(); // this raises a TypeError + var foo = function() {}; + +Due to the fact that `var` is a declaration, that hoists the variable name `foo` +before the actual execution of the code starts, `foo` is already defined when +the script gets executed. + +But since assignments only happen at runtime, the value of `foo` will default +to [undefined](#core.undefined) before the corresponding code is executed. + +### Named Function Expression + +Another special case is the assignment of named functions. + + var foo = function bar() { + bar(); // Works + } + bar(); // ReferenceError + +Here `bar` is not available in the outer scope, since the function only gets +assigned to `foo`; however, inside of `bar` it is available. This is due to +how [name resolution](#function.scopes) in JavaScript works, the name of the +function is *always* made available in the local scope of the function itself. + diff --git a/doc/ja/function/scopes.md b/doc/ja/function/scopes.md new file mode 100644 index 00000000..7ae5e4f5 --- /dev/null +++ b/doc/ja/function/scopes.md @@ -0,0 +1,231 @@ +## Scopes and Namespaces + +Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does **not** support block scope; hence, all that is left +is in the language is *function scope*. + + function test() { // a scope + for(var i = 0; i < 10; i++) { // not a scope + // count + } + console.log(i); // 10 + } + +> **Note:** When not used in an assignment, return statement or as a function +> argument, the `{...}` notation will get interpreted as a block statement and +> **not** as an object literal. This, in conjunction with +> [automatic insertion of semicolons](#core.semicolon), can lead to subtle errors. + +There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one *globally shared* namespace. + +Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a `ReferenceError`. + +### The Bane of Global Variables + + // script A + foo = '42'; + + // script B + var foo = '42' + +The above two scripts do **not** have the same effect. Script A defines a +variable called `foo` in the *global* scope and script B defines a `foo` in the +*current* scope. + +Again, that is **not** at all the *same effect*, not using `var` can have major +implications. + + // global scope + var foo = 42; + function test() { + // local scope + foo = 21; + } + test(); + foo; // 21 + +Leaving out the `var` statement inside the function `test` will override the +value of `foo`. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using `var` will introduce horrible and +hard to track down bugs. + + // global scope + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope of subLoop + for(i = 0; i < 10; i++) { // missing var statement + // do amazing stuff! + } + } + +The outer loop will terminate after the first call to `subLoop`, since `subLoop` +overwrites the global value of `i`. Using a `var` for the second `for` loop would +have easily avoided this error. The `var` statement should **never** be left out +unless the *desired effect* is to affect the outer scope. + +### Local Variables + +The only source for local variables in JavaScript are +[function](#function.general) parameters and variables that were declared via the +`var` statement. + + // global scope + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // local scope of the function test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +While `foo` and `i` are local variables inside the scope of the function `test`, +the assignment of `bar` will override the global variable with the same name. + +### Hoisting + +JavaScript **hoists** declarations. This means that both `var` statements and +`function` declarations will be moved to the top of their enclosing scope. + + 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]; + } + } + +The above code gets transformed before any execution is started. JavaScript moves +the `var` statements as well as the `function` declarations to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declartion got moved up too + function test(data) { + var goo, i, e; // missing block scope moves these here + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fails with a TypeError since bar is still 'undefined' + someValue = 42; // assignments are not affected by hoisting + bar = function() {}; + + test(); + +Missing block scoping will not only move `var` statements out of loops and +their bodies, it will also make the results of certain `if` constructs +non-intuitive. + +In the original code the `if` statement seemed to modify the *global +variable* `goo`, while actually it modifies the *local variable* - after hoisting +has been applied. + +Without the knowledge about *hoisting*, below code might seem to raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initiliazed + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, the above works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initiliaze SomeImportantThing here, or not + + // make sure it's there + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Name Resolution Order + +All scopes in JavaScript, including the *global scope*, have the special name +[`this`](#function.this) defined in them, which refers to the *current object*. + +Function scopes also have the name [`arguments`](#function.arguments) defined in +them which contains the arguments that were passed to a function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will lookup the name in the following order: + + 1. In case there is a `var foo` statement in the current scope use that. + 2. If one of the function parameters is named `foo` use that. + 3. If the function itself is called `foo` use that. + 4. Go to the next outer scope and start with **#1** again. + +> **Note:** Having a parameter called `arguments` will **prevent** the creation +> of the default `arguments` object. + +### Namespaces + +A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of *anonymous wrappers*. + + (function() { + // a self contained "namespace" + + window.foo = function() { + // an exposed closure + }; + + })(); // execute the function immediately + + +Unnamed functions are considered [expressions](#function.general); so in order to +being callable, they must first be evaluated. + + ( // evaluate the function inside the paranthesis + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way. + + // Two other ways + +function(){}(); + (function(){}()); + +### In Conclusion + +It is recommended to always use an *anonymous wrapper* for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs. + +Additionally, the use of global variables is considered **bad practice**. **Any** +use of them indicates badly written code that is prone to errors and hard to maintain. + diff --git a/doc/ja/function/this.md b/doc/ja/function/this.md new file mode 100644 index 00000000..85b11f0f --- /dev/null +++ b/doc/ja/function/this.md @@ -0,0 +1,111 @@ +## How `this` Works + +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. + +### The Global Scope + + this; + +When using `this` in global scope, it will simply refer to the *global* object. + + +### Calling a Function + + foo(); + +Here `this` will again refer to the *global* object. + +> **ES5 Note:** In strict mode, the global case **no longer** exists. +> `this` will instead have the value of `undefined` in that case. + +### Calling a Method + + test.foo(); + +In this example `this` will refer to `test`. + +### Calling a Constructor + + new foo(); + +A function call that is preceded by the `new` keyword acts as +a [constructor](#function.constructors). Inside the function `this` will refer +to a *newly created* `Object`. + +### Explicit Setting of `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array will expand to the below + foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 + +When using the `call` or `apply` methods of `Function.prototype`, the value of +`this` inside the called function gets **explicitly set** to the first argument +of the corresponding function call. + +As a result, the above example the *method case* does **not** apply, and `this` +inside of `foo` will be set to `bar`. + +> **Note:** `this` **cannot** be used to refer to the object inside of an `Object` +> literal. So `var obj = {me: this}` will **not** result in `me` referring to +> `obj`, since `this` only gets bound by one of the five listed cases. + +### Common Pitfalls + +While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it **never** has any practical use. + + Foo.method = function() { + function test() { + // this is set to the global object + } + test(); + } + +A common misconception is that `this` inside of `test` refers to `Foo`, while in +fact it **does not**. + +In order to gain access to `Foo` from within `test` it is necessary to create a +local variable inside of `method` which refers to `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Use that instead of this here + } + test(); + } + +`that` is just a normal variable name, but it is commonly used for the reference to an +outer `this`. In combination with [closures](#function.closures), it can also +be used to pass `this` values around. + +### Assigning Methods + +Another thing that does **not** work in JavaScript is function aliasing, that is, +**assigning** a method to a variable. + + var test = someObject.methodTest; + test(); + +Due to the first case `test` now acts like a plain function call; therefore, +`this` inside it will no longer refer to `someObject`. + +While the late binding of `this` might seem like a bad idea at first, it is in +fact what makes [prototypal inheritance](#object.prototype) work. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +When `method` gets called on a instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/doc/ja/index.json b/doc/ja/index.json new file mode 100644 index 00000000..d4012ed9 --- /dev/null +++ b/doc/ja/index.json @@ -0,0 +1,68 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in English", + "description": "A Guide to JavaScript's Quirks and Flaws.", + "sections": [ + { + "title": "Intro", + "dir": "intro", + "articles": [] + }, + { + "title": "Objects", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Functions", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Types", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Core", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Other", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md new file mode 100644 index 00000000..6ddd7c71 --- /dev/null +++ b/doc/ja/intro/index.md @@ -0,0 +1,47 @@ +## Intro + +**JavaScript Garden** is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language. + +JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent [guide][1] on the Mozilla Developer Network. + +## The Authors + +This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] +(Writing) and [Zhang Yi Jiang][4] (Design). + +## Contributors + + - [Caio Romão][5] (Spelling corrections) + - [Andreas Blixt][6] (Language corrections) + +## Hosting + +JavaScript Garden is hosted on GitHub, but [Cramer Development][7] supports us +with a mirror at [JavaScriptGarden.info][8]. + +## License + +JavaScript Garden is published under the [MIT license][9] and hosted on +[GitHub][10]. If you find errors or typos please [file an issue][11] or a pull +request on the repository. You can also find us in the [JavaScript room][12] on +Stack Overflow chat. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: http://stackoverflow.com/ +[3]: http://stackoverflow.com/users/170224/ivo-wetzel +[4]: http://stackoverflow.com/users/313758/yi-jiang +[5]: https://github.com/caio +[6]: https://github.com/blixt +[7]: http://cramerdev.com/ +[8]: http://javascriptgarden.info/ +[9]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[10]: https://github.com/BonsaiDen/JavaScript-Garden +[11]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript diff --git a/doc/ja/object/forinloop.md b/doc/ja/object/forinloop.md new file mode 100644 index 00000000..30751ed9 --- /dev/null +++ b/doc/ja/object/forinloop.md @@ -0,0 +1,51 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop also traverses the prototype +chain when iterating over the properties of an object. + +> **Note:** The `for in` loop will **not** iterate over any properties that +> have their `enumerable` attribute set to `false`; for example, the `length` +> property of an array. + + // Poisoning Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // prints both bar and moo + } + +Since it is not possible to change the behavior of the `for in` loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the [`hasOwnProperty`](#object.hasownproperty) method of +`Object.prototype`. + +> **Note:** Since the `for in` always traverses the complete prototype chain, it +> will get slower with each additional layer of inheritance added to an object. + +### Using `hasOwnProperty` for Filtering + + // still the foo from above + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +This version is the only correct one to use. Due to the use of `hasOwnProperty` it +will **only** print out `moo`. When `hasOwnProperty` is left out, the code is +prone to errors in cases where the native prototypes - e.g. `Object.prototype` - +have been extended. + +One widely used framework which does this is [Prototype][1]. When this +framework is included, `for in` loops that do not use `hasOwnProperty` are +guaranteed to break. + +### In Conclusion + +It is recommended to **always** use `hasOwnProperty`. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not. + +[1]: http://www.prototypejs.org/ + diff --git a/doc/ja/object/general.md b/doc/ja/object/general.md new file mode 100644 index 00000000..cbad5f11 --- /dev/null +++ b/doc/ja/object/general.md @@ -0,0 +1,99 @@ +## Object Usage and Properties + +Everything in JavaScript acts like an object, with the only two exceptions being +[`null`](#core.undefined) and [`undefined`](#core.undefined). + + false.toString() // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the *dot +notation* on a number as a floating point literal. + + 2.toString(); // raises SyntaxError + +There are a couple of workarounds which can be used in order make number +literals act as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left to the dot + (2).toString(); // 2 is evaluated first + +### Objects as a Data Type + +Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist +of named properties mapping to values. + +Using a object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +has no [own properties](#object.hasownproperty) defined on it. + + var foo = {}; // a new empty object + + // a new object with a property called 'test' with value 12 + var bar = {test: 12}; + +### Accessing Properties + +The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation. + + var foo = {name: 'Kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error. + +### Deleting Properties + +The only way to actually remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only remove the +*value* associated with the property, but not the *key*. + + 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]); + } + } + +The above outputs both `bar undefined` and `foo null` - only `baz` was +removed and is therefore missing from the output. + +### Notation of Keys + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // raises SyntaxError + }; + +Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a `SyntaxError` prior to ECMAScript 5. + +This error arises from the fact that `delete` is a *keyword*; therefore, it must be +notated as a *string literal* to ensure that it will be correctly interpreted by +older JavaScript engines. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/doc/ja/object/hasownproperty.md b/doc/ja/object/hasownproperty.md new file mode 100644 index 00000000..319a8801 --- /dev/null +++ b/doc/ja/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `hasOwnProperty` + +In order to check whether a object has a property defined on *itself* and **not** +somewhere on its [prototype chain](#object.prototype), it is necessary to use the +`hasOwnProperty` method which all objects inherit from `Object.prototype`. + +> **Note:** It is **not** enough to check whether a property is `undefined`. The +> property might very well exist, but its value just happens to be set to +> `undefined`. + +`hasOwnProperty` is the only thing in JavaScript which deals with properties and +does **not** traverse the prototype chain. + + // Poisoning 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 + +Only `hasOwnProperty` will give the correct and expected result, this is +essential when iterating over the properties of any object. There is **no** other +way to exclude properties that are not defined on the object *itself*, but +somewhere on its prototype chain. + +### `hasOwnProperty` as a Property + +JavaScript does **not** protect the property name `hasOwnProperty`; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an *external* `hasOwnProperty` in order to get correct results. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // always returns false + + // Use another Object's hasOwnProperty and call it with 'this' set to foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### In Conclusion + +When checking for the existence of a property on a object, `hasOwnProperty` is +the **only** method of doing so. It is also recommended to make `hasOwnProperty` +part of **every** [`for in` loop](#object.forinloop), this will avoid errors from +extended native [prototypes](#object.prototype). + diff --git a/doc/ja/object/prototype.md b/doc/ja/object/prototype.md new file mode 100644 index 00000000..f780eba2 --- /dev/null +++ b/doc/ja/object/prototype.md @@ -0,0 +1,116 @@ +## The Prototype + +JavaScript does not feature a classical inheritance model, instead it uses a +*prototypal* one. + +While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task. + +Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models. + +The first major difference is that inheritance in JavaScript is done by using so +called *prototype chains*. + +> **Note:** Simply using `Bar.prototype = Foo.prototype` will result in both objects +> sharing the **same** prototype. Therefore, changes to either object's prototype +> will affect the prototype of the other as well, which in most cases is not the +> desired effect. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Set Bar's prototype to a new instance of Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Make sure to list Bar as the actual constructor + Bar.prototype.constructor = Bar; + + var test = new Bar() // create a new bar instance + + // The resulting prototype chain + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World' } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the above, the object `test` will inherit from both `Bar.prototype` and +`Foo.prototype`; hence, it will have access to the function `method` that was +defined on `Foo`. It will also have access to the property `value` of the +**one** `Foo` instance that is its prototype. It is important to note that `new +Bar()` does **not** create a new `Foo` instance, but reuses the one assigned to +its prototype; thus, all `Bar` instances will share the **same** `value` property. + +> **Note:** Do **not** use `Bar.prototype = Foo`, since it will not point to +> the prototype of `Foo` but rather to the function object `Foo`. So the +> prototype chain will go over `Function.prototype` and not `Foo.prototype`; +> therefore, `method` will not be on the prototype chain. + +### Property Lookup + +When accessing the properties of an object, JavaScript will traverse the +prototype chain **upwards** until it finds a property with the requested name. + +When it reaches the top of the chain - namely `Object.prototype` - and still +hasn't found the specified property, it will return the value +[undefined](#core.undefined) instead. + +### The Prototype Property + +While the prototype property is used by the language to build the prototype +chains, it is still possible to assign **any** given value to it. Although +primitives will simply get ignored when assigned as a prototype. + + function Foo() {} + Foo.prototype = 1; // no effect + +Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains. + +### Performance + +The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain. + +Also, when [iterating](#object.forinloop) over the properties of an object +**every** property that is on the prototype chain will get enumerated. + +### Extension of Native Prototypes + +One mis-feature that is often used is to extend `Object.prototype` or one of the +other built in prototypes. + +This technique is called [monkey patching][1] and breaks *encapsulation*. While +used by widely spread frameworks such as [Prototype][2], there is still no good +reason for cluttering built-in types with additional *non-standard* functionality. + +The **only** good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +[`Array.forEach`][3]. + +### In Conclusion + +It is a **must** to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should **never** be extended +unless it is for the sake of compatibility with newer JavaScript features. + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + diff --git a/doc/ja/other/timeouts.md b/doc/ja/other/timeouts.md new file mode 100644 index 00000000..d196a3b7 --- /dev/null +++ b/doc/ja/other/timeouts.md @@ -0,0 +1,155 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function by using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript Standard. They are +> implemented as part of the [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` gets called, it will return the ID of the timeout and schedule +`foo` to run in **approximately** one thousand milliseconds in the future. +`foo` will then get executed exactly **once**. + +Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by **no means** a safe bet that one +will get the exact delay that was specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, that means, that [`this`](#function.this) inside the called function +refers to that very object. + + function Foo() { + this.value = 42; + this.method = function() { + // this refers to the global object + console.log(this.value); // will log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** As `setTimeout` takes a **function object** as its first parameter, an +> often made mistake is to use `setTimeout(foo(), 1000)`, which will use the +> **return value** of the call `foo` and **not** `foo`. This is, most of the time, +> a silent error, since when the function returns `undefined` `setTimeout` will +> **not** raise any error. + +### Stacking Calls with `setInterval` + +While `setTimeout` only runs the function once, `setInterval` - as the name +suggests - will execute the function **every** `X` milliseconds. But its use is +discouraged. + +When code that is being executed blocks the timeout call, `setInterval` will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up. + + function foo(){ + // something that blocks for 1 second + } + setInterval(foo, 100); + +In the above code `foo` will get called once and will then block for one second. + +While `foo` blocks the code `setInterval` will still schedule further calls to +it. Now, when `foo` has finished, there will already be **ten** further calls to +it waiting for execution. + +### Dealing with Possible Blocking Code + +The easiest as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and it gives additional control.`foo` itself can now decide +whether it wants to run again or not. + +### Manually Clearing Timeouts + +Clearing timeouts and intervals works by passing the respective ID to +`clearTimeout` or `clearInterval`, depending which `set` function was used in +the first place. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Clearing all timeouts + +As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality. + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically. + +### Hidden use of `eval` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used, since it internally makes use of `eval`. + +> **Note:** Since the timeout functions are **not** specified by the ECMAScript +> standard, the exact workings when a string is passed to them might differ in +> various JavaScript implementations. For example, Microsoft's JScript makes use of +> the `Function` constructor in place of `eval`. + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +Since `eval` is not getting called [directly](#core.eval) in this case, the string +passed to `setTimeout` will get executed in the *global scope*; thus, it will +not use the local variable `foo` from the scope of `bar`. + +It is further recommended to **not** use a string for passing arguments to the +function that will get called by either of the timeout functions. + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1,2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, a, b, c)`, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). + +### In Conclusion + +**Never** should a string be used as the parameter of `setTimeout` or +`setInterval`. It is a clear sign of **really** bad code, when arguments need +to be supplied to the function that gets called. An *anonymous function* should +be passed that then takes care of the actual call. + +Further, the use of `setInterval` should be avoided since its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/doc/ja/types/casting.md b/doc/ja/types/casting.md new file mode 100644 index 00000000..15d84e74 --- /dev/null +++ b/doc/ja/types/casting.md @@ -0,0 +1,70 @@ +## Type Casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number.toString() is converted + // back to a number + + 10 == '10'; // Strings gets converted to Number + 10 == '+10 '; // More string madness + 10 == '010'; // And more + isNaN(null) == false; // null converts to 0 + // which of course is not NaN + + // These are false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** Number literals that start with a `0` are interpreted as octal +> (Base 8). Octal support for these has been **removed** in ECMAScript 5 strict +> mode. + +In order to avoid the above, use of the [strict equal operator](#types.equality) +is **highly** recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system. + +### Constructors of Built-In Types + +The constructors of the built in types like `Number` and `String` behave +differently when being used with the `new` keyword and without it. + + new Number(10) === 10; // False, Object and Number + Number(10) === 10; // True, Number and Number + new Number(10) + 0 === 10; // True, due to implicit conversion + +Using a built-in type like `Number` as a constructor will create a new `Number` +object, but leaving out the `new` keyword will make the `Number` function behave +like a converter. + +In addition, having literals or non-object values in there will result in even +more type coercion. + +The best option is to cast to one of the three possible types **explicitly**. + +### Casting to a String + + '' + 10 === '10'; // true + +By prepending a empty string a value can easily be casted to a string. + +### Casting to a Number + + +'10' === 10; // true + +Using the **unary** plus operator it is possible to cast to a number. + +### Casting to a Boolean + +By using the **not** operator twice, a value can be converted a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/doc/ja/types/equality.md b/doc/ja/types/equality.md new file mode 100644 index 00000000..a578b19c --- /dev/null +++ b/doc/ja/types/equality.md @@ -0,0 +1,71 @@ +## Equality and Comparisons + +JavaScript has two different ways of comparing the values of objects for equality. + +### The Equality Operator + +The equality operator consists of two equal signs: `==` + +JavaScript features *weak typing*. This means that the equality operator +**coerces** types in order to compare them. + + "" == "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 + +The above table shows the results of the type coercion and it is the main reason +why the use of `==` is widely regarded as bad practice, it introduces hard to +track down bugs due to its complicated conversion rules. + +Additionally there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number. + +### The Strict Equality Operator + +The strict equality operator consists of **three** equal signs: `===` + +It works exactly like the normal equality operator, except that strict equality +operator does **not** perform type coercion between its operands. + + "" === "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 + +The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types. + +### Comparing Objects + +While both `==` and `===` are stated as **equality** operators, they behave +different when at least one of their operands happens to be an `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Here both operators compare for **identity** and **not** equality; that is, they +will compare for the same **instance** of the object, much like `is` in Python +and pointer comparison in C. + +### In Conclusion + +It is highly recommended to only use the **strict equality** operator. In cases +where types need to be coerced, it should be done [explicitly](#types.casting) +and not left to the language's complicated coercion rules. + diff --git a/doc/ja/types/instanceof.md b/doc/ja/types/instanceof.md new file mode 100644 index 00000000..8711331a --- /dev/null +++ b/doc/ja/types/instanceof.md @@ -0,0 +1,38 @@ +## The `instanceof` Operator + +The `instanceof` operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the [typeof operator](#types.typeof). + +### Comparing Custom Objects + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // This just sets Bar.prototype to the function object Foo + // But not to an actual instance of Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Using `instanceof` with Native Types + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +One important thing to note here is, that `instanceof` does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object. + +### In Conclusion + +The `instanceof` operator should **only** be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/doc/ja/types/typeof.md b/doc/ja/types/typeof.md new file mode 100644 index 00000000..e4b28d7f --- /dev/null +++ b/doc/ja/types/typeof.md @@ -0,0 +1,87 @@ +## The `typeof` Operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is near of being **completely broken**. + +Although `instanceof` still has its limited uses, `typeof` really has only one +practical use case, which does **not** happen to be checking the type of an +object. + +> **Note:** While `typeof` can also be called with a function like syntax +> i.e. `typeof(obj)`, this is not a function call. The two parenthesis will +> behave like normal and the return value will be used as the operand of the +> `typeof` operator. There is **no** `typeof` function. + +### The JavaScript Type Table + + Value Class Type + ------------------------------------- + "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 in Nitro/V8) + new RegExp("meow") RegExp object (function in Nitro/V8) + {} Object object + new Object() Object object + +In the above table *Type* refers to the value, that the `typeof` operator returns. +As can be clearly seen, this value is anything but consistent. + +The *Class* refers to the value of the internal `[[Class]]` property of an object. + +> **From the Specification:** The value of `[[Class]]` can be one of the +> following strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +In order to retrieve the value of `[[Class]]` one has to make use of the +`toString` method of `Object.prototype`. + +### The Class of an Object + +The specification gives exactly one way of accessing the `[[Class]]` value, +with the use of `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 + +In the above example, `Object.prototype.toString` gets called with the value of +[this](#function.this) being set to the object whose `[[Class]]` value should be +retrieved. + +> **ES5 Note:** For convenience the return value of `Object.prototype.toString` +> for both `null` and `undefined` was **changed** from `Object` to `Null` and +> `Undefined` in ECMAScript 5. + +### Testing for Undefined Variables + + typeof foo !== 'undefined' + +The above will check whether `foo` was actually declared or not; just +referencing it would result in a `ReferenceError`. This is the only thing +`typeof` is actually useful for. + +### In Conclusion + +In order to check the type of an object, it is highly recommended to use +`Object.prototype.toString`; as this is the only reliable way of doing so. +As shown in the above type table, some return values of `typeof` are not defined +in the specification; thus, they can differ across various implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided at +**all costs**. + + From 2a24fa87affeb63234d274a7b6525c6eddc05fdd Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:12:15 +0900 Subject: [PATCH 015/469] translate index.json to Japanese --- doc/ja/index.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index d4012ed9..4f263875 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -1,64 +1,64 @@ { "title": "JavaScript Garden", - "langTitle": "JavaScript Garden in English", - "description": "A Guide to JavaScript's Quirks and Flaws.", + "langTitle": "JavaScript Garden in Japanese", + "description": "JavaScriptの奇抜さと欠陥についてのガイドライン", "sections": [ { - "title": "Intro", + "title": "イントロ", "dir": "intro", "articles": [] }, { - "title": "Objects", + "title": "オブジェクト", "dir": "object", "articles": [ - "general", + "概論", "prototype", "hasownproperty", - "forinloop" + "forinループ" ] }, { - "title": "Functions", + "title": "関数", "dir": "function", "articles": [ - "general", + "概論", "this", - "closures", - "arguments", - "constructors", - "scopes" + "クロージャ", + "引数", + "コンストラクタ", + "スコープ" ] }, { - "title": "Arrays", + "title": "配列", "dir": "array", "articles": [ - "general", - "constructor" + "概論", + "コンストラクタ" ] }, { - "title": "Types", + "title": "型", "dir": "types", "articles": [ - "equality", + "同値・同価", "typeof", "instanceof", - "casting" + "型変換" ] }, { - "title": "Core", + "title": "コア", "dir": "core", "articles": [ "eval", "undefined", - "semicolon" + "セミコロン" ] }, { - "title": "Other", + "title": "その他", "dir": "other", "articles": [ "timeouts" From d51d1246640443ababb84c5ce94057465e96da0d Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:13:43 +0900 Subject: [PATCH 016/469] add ja to language.json --- doc/language.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/language.json b/doc/language.json index 1c1bdffc..56774f29 100644 --- a/doc/language.json +++ b/doc/language.json @@ -1,5 +1,5 @@ { "default": "en", - "listed": ["en", "fi", "ru", "zh", "tr", "pl"] + "listed": ["en", "fi", "ru", "zh", "tr", "pl", "ja"] } From 5f190cdb71b117540deaa9f4b3b32da68a30b52a Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:24:31 +0900 Subject: [PATCH 017/469] fix ariticles --- doc/ja/index.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index 4f263875..85e25a6b 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -12,40 +12,40 @@ "title": "オブジェクト", "dir": "object", "articles": [ - "概論", + "general", "prototype", "hasownproperty", - "forinループ" + "forinloop" ] }, { "title": "関数", "dir": "function", "articles": [ - "概論", + "general", "this", - "クロージャ", - "引数", - "コンストラクタ", - "スコープ" + "closures", + "arguments", + "constructors", + "scopes" ] }, { "title": "配列", "dir": "array", "articles": [ - "概論", - "コンストラクタ" + "general", + "constructor" ] }, { "title": "型", "dir": "types", "articles": [ - "同値・同価", + "equality", "typeof", "instanceof", - "型変換" + "casting" ] }, { @@ -54,7 +54,7 @@ "articles": [ "eval", "undefined", - "セミコロン" + "semicolon" ] }, { From 91aa9d74bc9dcf5a2c177420a575ffbaf4b816ff Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 22:28:29 +0900 Subject: [PATCH 018/469] build ja/index.html --- site/ja/index.html | 1878 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1878 insertions(+) create mode 100644 site/ja/index.html diff --git a/site/ja/index.html b/site/ja/index.html new file mode 100644 index 00000000..60e5d1c9 --- /dev/null +++ b/site/ja/index.html @@ -0,0 +1,1878 @@ +JavaScript Garden +

イントロ

JavaScript Garden is a growing collection of documentation about the most +quirky parts of the JavaScript programming language. It gives advice to +avoid common mistakes, subtle bugs, as well as performance issues and bad +practices that non-expert JavaScript programmers may encounter on their +endeavours into the depths of the language.

+ +

JavaScript Garden does not aim to teach you JavaScript. Former knowledge +of the language is strongly recommended in order to understand the topics covered +in this guide. In order to learn the basics of the language, please head over to +the excellent guide on the Mozilla Developer Network.

+ +

The Authors

+ +

This guide is the work of two lovely Stack Overflow users, Ivo Wetzel +(Writing) and Zhang Yi Jiang (Design).

+ +

Contributors

+ + + +

Hosting

+ +

JavaScript Garden is hosted on GitHub, but Cramer Development supports us +with a mirror at JavaScriptGarden.info.

+ +

License

+ +

JavaScript Garden is published under the MIT license and hosted on +GitHub. If you find errors or typos please file an issue or a pull +request on the repository. You can also find us in the JavaScript room on +Stack Overflow chat.

オブジェクト

Object Usage and Properties

Everything in JavaScript acts like an object, with the only two exceptions being +null and undefined.

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

A common misconception is that number literals cannot be used as +objects. That is because a flaw in JavaScript's parser tries to parse the dot +notation on a number as a floating point literal.

+ +
2.toString(); // raises SyntaxError
+
+ +

There are a couple of workarounds which can be used in order make number +literals act as objects too.

+ +
2..toString(); // the second point is correctly recognized
+2 .toString(); // note the space left to the dot
+(2).toString(); // 2 is evaluated first
+
+ +

Objects as a Data Type

+ +

Objects in JavaScript can also be used as a Hashmap, they mainly consist +of named properties mapping to values.

+ +

Using a object literal - {} notation - it is possible to create a +plain object. This new object inherits from Object.prototype and +has no own properties defined on it.

+ +
var foo = {}; // a new empty object
+
+// a new object with a property called 'test' with value 12
+var bar = {test: 12}; 
+
+ +

Accessing Properties

+ +

The properties of an object can be accessed in two ways, via either the dot +notation, or the square bracket notation.

+ +
var foo = {name: 'Kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // works
+
+ +

Both notations are identical in their workings, with the only difference being that +the square bracket notation allows for dynamic setting of properties, as well as +the use of property names that would otherwise lead to a syntax error.

+ +

Deleting Properties

+ +

The only way to actually remove a property from an object is to use the delete +operator; setting the property to undefined or null only remove the +value associated with the property, but not the key.

+ +
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]);
+    }
+}
+
+ +

The above outputs both bar undefined and foo null - only baz was +removed and is therefore missing from the output.

+ +

Notation of Keys

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

Object properties can be both notated as plain characters and as strings. Due to +another mis-design in JavaScript's parser, the above will throw +a SyntaxError prior to ECMAScript 5.

+ +

This error arises from the fact that delete is a keyword; therefore, it must be +notated as a string literal to ensure that it will be correctly interpreted by +older JavaScript engines.

The Prototype

JavaScript does not feature a classical inheritance model, instead it uses a +prototypal one.

+ +

While this is often considered to be one of JavaScript's weaknesses, the +prototypal inheritance model is in fact more powerful than the classic model. +It is for example fairly trivial to build a classic model on top of it, while the +other way around is a far more difficult task.

+ +

Due to the fact that JavaScript is basically the only widely used language that +features prototypal inheritance, it takes some time to adjust to the +differences between the two models.

+ +

The first major difference is that inheritance in JavaScript is done by using so +called prototype chains.

+ + + +
function Foo() {
+    this.value = 42;
+}
+Foo.prototype = {
+    method: function() {}
+};
+
+function Bar() {}
+
+// Set Bar's prototype to a new instance of Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Make sure to list Bar as the actual constructor
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // create a new bar instance
+
+// The resulting prototype chain
+test [instance of Bar]
+    Bar.prototype [instance of Foo] 
+        { foo: 'Hello World' }
+        Foo.prototype
+            { method: ... }
+            Object.prototype
+                { toString: ... /* etc. */ }
+
+ +

In the above, the object test will inherit from both Bar.prototype and +Foo.prototype; hence, it will have access to the function method that was +defined on Foo. It will also have access to the property value of the +one Foo instance that is its prototype. It is important to note that new +Bar() does not create a new Foo instance, but reuses the one assigned to +its prototype; thus, all Bar instances will share the same value property.

+ + + +

Property Lookup

+ +

When accessing the properties of an object, JavaScript will traverse the +prototype chain upwards until it finds a property with the requested name.

+ +

When it reaches the top of the chain - namely Object.prototype - and still +hasn't found the specified property, it will return the value +undefined instead.

+ +

The Prototype Property

+ +

While the prototype property is used by the language to build the prototype +chains, it is still possible to assign any given value to it. Although +primitives will simply get ignored when assigned as a prototype.

+ +
function Foo() {}
+Foo.prototype = 1; // no effect
+
+ +

Assigning objects, as shown in the example above, will work, and allows for dynamic +creation of prototype chains.

+ +

Performance

+ +

The lookup time for properties that are high up on the prototype chain can have a +negative impact on performance critical sections of code. Additionally, trying to +access non-existent properties will always traverse the full prototype chain.

+ +

Also, when iterating over the properties of an object +every property that is on the prototype chain will get enumerated.

+ +

Extension of Native Prototypes

+ +

One mis-feature that is often used is to extend Object.prototype or one of the +other built in prototypes.

+ +

This technique is called monkey patching and breaks encapsulation. While +used by widely spread frameworks such as Prototype, there is still no good +reason for cluttering built-in types with additional non-standard functionality.

+ +

The only good reason for extending a built-in prototype is to backport +the features of newer JavaScript engines; for example, +Array.forEach.

+ +

In Conclusion

+ +

It is a must to understand the prototypal inheritance model completely +before writing complex code which makes use of it. Also, watch the length of +the prototype chains and break them up if necessary to avoid possible +performance issues. Further, the native prototypes should never be extended +unless it is for the sake of compatibility with newer JavaScript features.

hasOwnProperty

In order to check whether a object has a property defined on itself and not +somewhere on its prototype chain, it is necessary to use the +hasOwnProperty method which all objects inherit from Object.prototype.

+ + + +

hasOwnProperty is the only thing in JavaScript which deals with properties and +does not traverse the prototype chain.

+ +
// Poisoning 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
+
+ +

Only hasOwnProperty will give the correct and expected result, this is +essential when iterating over the properties of any object. There is no other +way to exclude properties that are not defined on the object itself, but +somewhere on its prototype chain.

+ +

hasOwnProperty as a Property

+ +

JavaScript does not protect the property name hasOwnProperty; thus, if the +possibility exists that an object might have a property with this name, it is +necessary to use an external hasOwnProperty in order to get correct results.

+ +
var foo = {
+    hasOwnProperty: function() {
+        return false;
+    },
+    bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // always returns false
+
+// Use another Object's hasOwnProperty and call it with 'this' set to foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+ +

In Conclusion

+ +

When checking for the existence of a property on a object, hasOwnProperty is +the only method of doing so. It is also recommended to make hasOwnProperty +part of every for in loop, this will avoid errors from +extended native prototypes.

The for in Loop

Just like the in operator, the for in loop also traverses the prototype +chain when iterating over the properties of an object.

+ + + +
// Poisoning Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+    console.log(i); // prints both bar and moo
+}
+
+ +

Since it is not possible to change the behavior of the for in loop itself, it +is necessary to filter out the unwanted properties inside the loop body , +this is done by using the hasOwnProperty method of +Object.prototype.

+ + + +

Using hasOwnProperty for Filtering

+ +
// still the foo from above
+for(var i in foo) {
+    if (foo.hasOwnProperty(i)) {
+        console.log(i);
+    }
+}
+
+ +

This version is the only correct one to use. Due to the use of hasOwnProperty it +will only print out moo. When hasOwnProperty is left out, the code is +prone to errors in cases where the native prototypes - e.g. Object.prototype - +have been extended.

+ +

One widely used framework which does this is Prototype. When this +framework is included, for in loops that do not use hasOwnProperty are +guaranteed to break.

+ +

In Conclusion

+ +

It is recommended to always use hasOwnProperty. Never should any +assumptions be made about the environment the code is running in, or whether the +native prototypes have been extended or not.

関数

Function Declarations and Expressions

Functions in JavaScript are first class objects. That means they can be +passed around like any other value. One common use of this feature is to pass +an anonymous function as a callback to another, possibly asynchronous function.

+ +

The function Declaration

+ +
function foo() {}
+
+ +

The above function gets hoisted before the execution of the +program starts; thus, it is available everywhere in the scope it was defined +in, even if called before the actual definition in the source.

+ +
foo(); // Works because foo was created before this code runs
+function foo() {}
+
+ +

The function Expression

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

This example assigns the unnamed and anonymous function to the variable foo.

+ +
foo; // 'undefined'
+foo(); // this raises a TypeError
+var foo = function() {};
+
+ +

Due to the fact that var is a declaration, that hoists the variable name foo +before the actual execution of the code starts, foo is already defined when +the script gets executed.

+ +

But since assignments only happen at runtime, the value of foo will default +to undefined before the corresponding code is executed.

+ +

Named Function Expression

+ +

Another special case is the assignment of named functions.

+ +
var foo = function bar() {
+    bar(); // Works
+}
+bar(); // ReferenceError
+
+ +

Here bar is not available in the outer scope, since the function only gets +assigned to foo; however, inside of bar it is available. This is due to +how name resolution in JavaScript works, the name of the +function is always made available in the local scope of the function itself.

How this Works

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.

+ +

The Global Scope

+ +
this;
+
+ +

When using this in global scope, it will simply refer to the global object.

+ +

Calling a Function

+ +
foo();
+
+ +

Here this will again refer to the global object.

+ + + +

Calling a Method

+ +
test.foo(); 
+
+ +

In this example this will refer to test.

+ +

Calling a Constructor

+ +
new foo(); 
+
+ +

A function call that is preceded by the new keyword acts as +a constructor. Inside the function this will refer +to a newly created Object.

+ +

Explicit Setting of this

+ +
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // array will expand to the below
+foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
+
+ +

When using the call or apply methods of Function.prototype, the value of +this inside the called function gets explicitly set to the first argument +of the corresponding function call.

+ +

As a result, the above example the method case does not apply, and this +inside of foo will be set to bar.

+ + + +

Common Pitfalls

+ +

While most of these cases make sense, the first one is to be considered another +mis-design of the language, as it never has any practical use.

+ +
Foo.method = function() {
+    function test() {
+        // this is set to the global object
+    }
+    test();
+}
+
+ +

A common misconception is that this inside of test refers to Foo, while in +fact it does not.

+ +

In order to gain access to Foo from within test it is necessary to create a +local variable inside of method which refers to Foo.

+ +
Foo.method = function() {
+    var that = this;
+    function test() {
+        // Use that instead of this here
+    }
+    test();
+}
+
+ +

that is just a normal variable name, but it is commonly used for the reference to an +outer this. In combination with closures, it can also +be used to pass this values around.

+ +

Assigning Methods

+ +

Another thing that does not work in JavaScript is function aliasing, that is, +assigning a method to a variable.

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

Due to the first case test now acts like a plain function call; therefore, +this inside it will no longer refer to someObject.

+ +

While the late binding of this might seem like a bad idea at first, it is in +fact what makes prototypal inheritance work.

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

When method gets called on a instance of Bar, this will now refer to that +very instance.

Closures and References

One of JavaScript's most powerful features is the availability of closures, +this means that scopes always keep access to the outer scope they were +defined in. Since the only scoping that JavaScript has is +function scope, all functions, by default, act as closures.

+ +

Emulating private variables

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

Here, Counter returns two closures. The function increment as well as +the function get. Both of these functions keep a reference to the scope of +Counter and, therefore, always keep access to the count variable that was +defined in that very scope.

+ +

Why Private Variables Work

+ +

Since it is not possible to reference or assign scopes in JavaScript, there is +no way of accessing the variable count from the outside. The only way to +interact with it is via the two closures.

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

The above code will not change the variable count in the scope of Counter, +since foo.hack was not defined in that scope. It will instead create - or +override - the global variable count.

+ +

Closures Inside Loops

+ +

One often made mistake is to use closures inside of loops, as if they were +copying the value of the loops index variable.

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

The above will not output the numbers 0 through 9, but will simply print +the number 10 ten times.

+ +

The anonymous function keeps a reference to i and at the time +console.log gets called, the for loop has already finished and the value of +i as been set to 10.

+ +

In order to get the desired behavior, it is necessary to create a copy of +the value of i.

+ +

Avoiding the Reference Problem

+ +

In order to copy the value of the loop's index variable, it is best to use an +anonymous wrapper.

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

The anonymous outer function gets called immediately with i as its first +argument and will receive a copy of the value of i as its parameter e.

+ +

The anonymous function that gets passed to setTimeout now has a reference to +e, whose value does not get changed by the loop.

+ +

There is another possible way of achieving this; that is to return a function +from the anonymous wrapper, that will then have the same behavior as the code +above.

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

The arguments Object

Every function scope in JavaScript can access the special variable arguments. +This variable holds a list of all the arguments that were passed to the function.

+ + + +

The arguments object is not an Array. While it has some of the +semantics of an array - namely the length property - it does not inherit from +Array.prototype and is in fact an Object.

+ +

Due to this, it is not possible to use standard array methods like push, +pop or slice on arguments. While iteration with a plain for loop works +just fine, it is necessary to convert it to a real Array in order to use the +standard Array methods on it.

+ +

Converting to an Array

+ +

The code below will return a new Array containing all the elements of the +arguments object.

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

This conversion is slow, it is not recommended to use it in performance +critical sections of code.

+ +

Passing Arguments

+ +

The following is the recommended way of passing arguments from one function to +another.

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

Another trick is to use both call and apply together to create fast, unbound +wrappers.

+ +
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+    console.log(this, a, b, c);
+};
+
+// Create an unbound version of "method" 
+// It takes the parameters: this, arg1, arg2...argN
+Foo.method = function() {
+
+    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
+    Function.call.apply(Foo.prototype.method, arguments);
+};
+
+ +

Formal Parameters and Arguments Indices

+ +

The arguments object creates getter and setter functions for both its +properties as well as the function's formal parameters.

+ +

As a result, changing the value of a formal parameter will also change the value +of the corresponding property on the arguments object, and the other way around.

+ +
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);
+
+ +

Performance Myths and Truths

+ +

The arguments object is always created with the only two exceptions being the +cases where it is declared as a name inside of a function or one of its formal +parameters. It does not matter whether it is used or not.

+ +

Both getters and setters are always created; thus, using it has nearly +no performance impact at all, especially not in real world code where there is +more than a simple access to the arguments object's properties.

+ + + +

However, there is one case which will drastically reduce the performance in +modern JavaScript engines. That case is the use of arguments.callee.

+ +
function foo() {
+    arguments.callee; // do something with this function object
+    arguments.callee.caller; // and the calling function object
+}
+
+function bigLoop() {
+    for(var i = 0; i < 100000; i++) {
+        foo(); // Would normally be inlined...
+    }
+}
+
+ +

In the above code, foo can no longer be a subject to inlining since it +needs to know about both itself and its caller. This not only defeats possible +performance gains that would arise from inlining, it also breaks encapsulation +since the function may now be dependent on a specific calling context.

+ +

It is highly recommended to never make use of arguments.callee or any of +its properties.

+ +

Constructors

Constructors in JavaScript are yet again different from many other languages. Any +function call that is preceded by the new keyword acts as a constructor.

+ +

Inside the constructor - the called function - the value of this refers to a +newly created Object. The prototype of this new +object is set to the prototype of the function object that was invoked as the +constructor.

+ +

If the function that was called has no explicit return statement, then it +implicitly returns the value of this - the new object.

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

The above calls Foo as constructor and sets the prototype of the newly +created object to Foo.prototype.

+ +

In case of an explicit return statement the function returns the value +specified that statement, but only if the return value is an Object.

+ +
function Bar() {
+    return 2;
+}
+new Bar(); // a new object
+
+function Test() {
+    this.value = 2;
+
+    return {
+        foo: 1
+    };
+}
+new Test(); // the returned object
+
+ +

When the new keyword is omitted, the function will not return a new object.

+ +
function Foo() {
+    this.bla = 1; // gets set on the global object
+}
+Foo(); // undefined
+
+ +

While the above example might still appear to work in some cases, due to the +workings of this in JavaScript, it will use the +global object as the value of this.

+ +

Factories

+ +

In order to be able to omit the new keyword, the constructor function has to +explicitly return a value.

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

Both calls to Bar return the exact same thing, a newly create object which +has a property called method on it, that is a +Closure.

+ +

It is also to note that the call new Bar() does not affect the prototype +of the returned object. While the prototype will be set on the newly created +object, Bar never returns that new object.

+ +

In the above example, there is no functional difference between using and +not using the new keyword.

+ +

Creating New Objects via Factories

+ +

An often made recommendation is to not use new since forgetting its use +may lead to bugs.

+ +

In order to create new object, one should rather use a factory and construct a +new object inside of that factory.

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

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. It uses more memory since the created objects do not share the methods +on a prototype.
  2. +
  3. In order to inherit the factory needs to copy all the methods from another +object or put that object on the prototype of the new object.
  4. +
  5. Dropping the prototype chain just because of a left out new keyword +somehow goes against the spirit of the language.
  6. +
+ +

In Conclusion

+ +

While omitting the new keyword might lead to bugs, it is certainly not a +reason to drop the use of prototypes altogether. In the end it comes down to +which solution is better suited for the needs of the application, it is +especially important to choose a specific style of object creation and stick +with it.

Scopes and Namespaces

Although JavaScript deals fine with the syntax of two matching curly +braces for blocks, it does not support block scope; hence, all that is left +is in the language is function scope.

+ +
function test() { // a scope
+    for(var i = 0; i < 10; i++) { // not a scope
+        // count
+    }
+    console.log(i); // 10
+}
+
+ + + +

There are also no distinct namespaces in JavaScript, that means that everything +gets defined in one globally shared namespace.

+ +

Each time a variable is referenced, JavaScript will traverse upwards through all +the scopes until it finds it. In the case that it reaches the global scope and +still has not found the requested name, it will raise a ReferenceError.

+ +

The Bane of Global Variables

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

The above two scripts do not have the same effect. Script A defines a +variable called foo in the global scope and script B defines a foo in the +current scope.

+ +

Again, that is not at all the same effect, not using var can have major +implications.

+ +
// global scope
+var foo = 42;
+function test() {
+    // local scope
+    foo = 21;
+}
+test();
+foo; // 21
+
+ +

Leaving out the var statement inside the function test will override the +value of foo. While this might not seem like a big deal at first, having +thousands of lines of JavaScript and not using var will introduce horrible and +hard to track down bugs.

+ +
// global scope
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+    subLoop();
+}
+
+function subLoop() {
+    // scope of subLoop
+    for(i = 0; i < 10; i++) { // missing var statement
+        // do amazing stuff!
+    }
+}
+
+ +

The outer loop will terminate after the first call to subLoop, since subLoop +overwrites the global value of i. Using a var for the second for loop would +have easily avoided this error. The var statement should never be left out +unless the desired effect is to affect the outer scope.

+ +

Local Variables

+ +

The only source for local variables in JavaScript are +function parameters and variables that were declared via the +var statement.

+ +
// global scope
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+    // local scope of the function test
+    i = 5;
+
+    var foo = 3;
+    bar = 4;
+}
+test(10);
+
+ +

While foo and i are local variables inside the scope of the function test, +the assignment of bar will override the global variable with the same name.

+ +

Hoisting

+ +

JavaScript hoists declarations. This means that both var statements and +function declarations will be moved to the top of their enclosing scope.

+ +
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];
+    }
+}
+
+ +

The above code gets transformed before any execution is started. JavaScript moves +the var statements as well as the function declarations to the top of the +nearest surrounding scope.

+ +
// var statements got moved here
+var bar, someValue; // default to 'undefined'
+
+// the function declartion got moved up too
+function test(data) {
+    var goo, i, e; // missing block scope moves these here
+    if (false) {
+        goo = 1;
+
+    } else {
+        goo = 2;
+    }
+    for(i = 0; i < 100; i++) {
+        e = data[i];
+    }
+}
+
+bar(); // fails with a TypeError since bar is still 'undefined'
+someValue = 42; // assignments are not affected by hoisting
+bar = function() {};
+
+test();
+
+ +

Missing block scoping will not only move var statements out of loops and +their bodies, it will also make the results of certain if constructs +non-intuitive.

+ +

In the original code the if statement seemed to modify the global +variable goo, while actually it modifies the local variable - after hoisting +has been applied.

+ +

Without the knowledge about hoisting, below code might seem to raise a +ReferenceError.

+ +
// check whether SomeImportantThing has been initiliazed
+if (!SomeImportantThing) {
+    var SomeImportantThing = {};
+}
+
+ +

But of course, the above works due to the fact that the var statement is being +moved to the top of the global scope.

+ +
var SomeImportantThing;
+
+// other code might initiliaze SomeImportantThing here, or not
+
+// make sure it's there
+if (!SomeImportantThing) {
+    SomeImportantThing = {};
+}
+
+ +

Name Resolution Order

+ +

All scopes in JavaScript, including the global scope, have the special name +this defined in them, which refers to the current object.

+ +

Function scopes also have the name arguments defined in +them which contains the arguments that were passed to a function.

+ +

For example, when trying to access a variable named foo inside the scope of a +function, JavaScript will lookup the name in the following order:

+ +
    +
  1. In case there is a var foo statement in the current scope use that.
  2. +
  3. If one of the function parameters is named foo use that.
  4. +
  5. If the function itself is called foo use that.
  6. +
  7. Go to the next outer scope and start with #1 again.
  8. +
+ + + +

Namespaces

+ +

A common problem of having only one global namespace is the likeliness of running +into problems where variable names clash. In JavaScript, this problem can +easily be avoided with the help of anonymous wrappers.

+ +
(function() {
+    // a self contained "namespace"
+
+    window.foo = function() {
+        // an exposed closure
+    };
+
+})(); // execute the function immediately
+
+ +

Unnamed functions are considered expressions; so in order to +being callable, they must first be evaluated.

+ +
( // evaluate the function inside the paranthesis
+function() {}
+) // and return the function object
+() // call the result of the evaluation
+
+ +

There are other ways for evaluating and calling the function expression; which, +while different in syntax, do behave the exact same way.

+ +
// Two other ways
++function(){}();
+(function(){}());
+
+ +

In Conclusion

+ +

It is recommended to always use an anonymous wrapper for encapsulating code in +its own namespace. This does not only protect code against name clashes, it +also allows for better modularization of programs.

+ +

Additionally, the use of global variables is considered bad practice. Any +use of them indicates badly written code that is prone to errors and hard to maintain.

配列

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use +the for in loop in for iteration on them. In fact there +are a number of good reasons against the use of for in on arrays.

+ + + +

Since the for in loop enumerates all the properties that are on the prototype +chain and the only way to exclude those properties is to use +hasOwnProperty, it is already up to twenty times +slower than a normal for loop.

+ +

Iteration

+ +

In order to achieve the best performance when iterating over arrays, it is best +to use the classic for loop.

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

There is one extra catch in the above example, that is the caching of the +length of the array via l = list.length.

+ +

Although the length property is defined on the array itself, there is still an +overhead for doing the lookup on each iteration of the loop. And while recent +JavaScript engines may apply optimization in this case, there is no way of +telling whether the code will run on one of these newer engines or not.

+ +

In fact, leaving out the caching may result in the loop being only half as +fast as with the cached length.

+ +

The length Property

+ +

While the getter of the length property simply returns the number of +elements that are contained in the array, the setter can be used to +truncate the array.

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

Assigning a smaller length does truncate the array, but increasing the length +does not have any effect on the array.

+ +

In Conclusion

+ +

For the best performance it is recommended to always use the plain for loop +and cache the length property. The use of for in on an array is a sign of +badly written code that is prone to bugs and bad performance.

The Array Constructor

Since the Array constructor is ambiguous in how it deals with its parameters, +it is highly recommended to always use the array literals - [] notation - +when creating new arrays.

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

In cases when there is only one argument passed to the Array constructor, +and that argument is a Number, the constructor will return a new sparse +array with the length property set to the value of the argument. It should be +noted that only the length property of the new array will be set this way, +the actual indexes of the array will not be initialized.

+ +
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, the index was not set
+
+ +

The behavior of being able to set the length of the array upfront only comes in +handy in a few cases, like repeating a string, in which it avoids the use of a +for loop code.

+ +
new Array(count + 1).join(stringToRepeat);
+
+ +

In Conclusion

+ +

The use of the Array constructor should be avoided as much as possible. +Literals are definitely preferred. They are shorter and have a clearer syntax; +therefore, they also increase the readability of the code.

Equality and Comparisons

JavaScript has two different ways of comparing the values of objects for equality.

+ +

The Equality Operator

+ +

The equality operator consists of two equal signs: ==

+ +

JavaScript features weak typing. This means that the equality operator +coerces types in order to compare them.

+ +
""           ==   "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
+
+ +

The above table shows the results of the type coercion and it is the main reason +why the use of == is widely regarded as bad practice, it introduces hard to +track down bugs due to its complicated conversion rules.

+ +

Additionally there is also a performance impact when type coercion is in play; +for example, a string has to be converted to a number before it can be compared +to another number.

+ +

The Strict Equality Operator

+ +

The strict equality operator consists of three equal signs: ===

+ +

It works exactly like the normal equality operator, except that strict equality +operator does not perform type coercion between its operands.

+ +
""           ===   "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
+
+ +

The above results are a lot clearer and allow for early breakage of code. This +hardens code to a certain degree and also gives performance improvements in case +the operands are of different types.

+ +

Comparing Objects

+ +

While both == and === are stated as equality operators, they behave +different when at least one of their operands happens to be an Object.

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

Here both operators compare for identity and not equality; that is, they +will compare for the same instance of the object, much like is in Python +and pointer comparison in C.

+ +

In Conclusion

+ +

It is highly recommended to only use the strict equality operator. In cases +where types need to be coerced, it should be done explicitly +and not left to the language's complicated coercion rules.

The typeof Operator

The typeof operator (together with +instanceof) is probably the biggest +design flaw of JavaScript, as it is near of being completely broken.

+ +

Although instanceof still has its limited uses, typeof really has only one +practical use case, which does not happen to be checking the type of an +object.

+ + + +

The JavaScript Type Table

+ +
Value               Class      Type
+-------------------------------------
+"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 in Nitro/V8)
+new RegExp("meow")  RegExp     object (function in Nitro/V8)
+{}                  Object     object
+new Object()        Object     object
+
+ +

In the above table Type refers to the value, that the typeof operator returns. +As can be clearly seen, this value is anything but consistent.

+ +

The Class refers to the value of the internal [[Class]] property of an object.

+ + + +

In order to retrieve the value of [[Class]] one has to make use of the +toString method of Object.prototype.

+ +

The Class of an Object

+ +

The specification gives exactly one way of accessing the [[Class]] value, +with the use of 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
+
+ +

In the above example, Object.prototype.toString gets called with the value of +this being set to the object whose [[Class]] value should be +retrieved.

+ + + +

Testing for Undefined Variables

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

The above will check whether foo was actually declared or not; just +referencing it would result in a ReferenceError. This is the only thing +typeof is actually useful for.

+ +

In Conclusion

+ +

In order to check the type of an object, it is highly recommended to use +Object.prototype.toString; as this is the only reliable way of doing so. +As shown in the above type table, some return values of typeof are not defined +in the specification; thus, they can differ across various implementations.

+ +

Unless checking whether a variable is defined, typeof should be avoided at +all costs.

The instanceof Operator

The instanceof operator compares the constructors of its two operands. It is +only useful when comparing custom made objects. Used on built-in types, it is +nearly as useless as the typeof operator.

+ +

Comparing Custom Objects

+ +
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo
+// But not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
+
+ +

Using instanceof with Native Types

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

One important thing to note here is, that instanceof does not work on objects +that origin from different JavaScript contexts (e.g. different documents +in a web browser), since their constructors will not be the exact same object.

+ +

In Conclusion

+ +

The instanceof operator should only be used when dealing with custom made +objects that origin from the same JavaScript context. Just like the +typeof operator, every other use of it should be avoided.

Type Casting

JavaScript is a weakly typed language, so it will apply type coercion +wherever possible.

+ +
// These are true
+new Number(10) == 10; // Number.toString() is converted
+                      // back to a number
+
+10 == '10';           // Strings gets converted to Number
+10 == '+10 ';         // More string madness
+10 == '010';          // And more 
+isNaN(null) == false; // null converts to 0
+                      // which of course is not NaN
+
+// These are false
+10 == 010;
+10 == '-10';
+
+ + + +

In order to avoid the above, use of the strict equal operator +is highly recommended. Although this avoids a lot of common pitfalls, there +are still many further issues that arise from JavaScript's weak typing system.

+ +

Constructors of Built-In Types

+ +

The constructors of the built in types like Number and String behave +differently when being used with the new keyword and without it.

+ +
new Number(10) === 10;     // False, Object and Number
+Number(10) === 10;         // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
+ +

Using a built-in type like Number as a constructor will create a new Number +object, but leaving out the new keyword will make the Number function behave +like a converter.

+ +

In addition, having literals or non-object values in there will result in even +more type coercion.

+ +

The best option is to cast to one of the three possible types explicitly.

+ +

Casting to a String

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

By prepending a empty string a value can easily be casted to a string.

+ +

Casting to a Number

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

Using the unary plus operator it is possible to cast to a number.

+ +

Casting to a Boolean

+ +

By using the not operator twice, a value can be converted a boolean.

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

コア

Why Not to Use eval

The eval function will execute a string of JavaScript code in the local scope.

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

But eval only executes in local scope when it is being called directly and +the name of the called function is actually eval.

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

The use of eval should be avoided at all costs. 99.9% of its "uses" can be +achieved without it.

+ +

eval in Disguise

+ +

The timeout functions setTimeout and setInterval can both +take a string as their first argument. This string will always get executed +in the global scope since eval is not being called directly in that case.

+ +

Security Issues

+ +

eval also is a security problem as it executes any code given to it, +it should never be used with strings of unknown or untrusted origins.

+ +

In Conclusion

+ +

eval should never be used, any code that makes use of it is to be questioned in +its workings, performance and security. In case something requires eval in +order to work, its design is to be questioned and should not be used in the +first place, a better design should be used, that does not require the use of +eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two +being undefined.

+ +

The Value undefined

+ +

undefined is a type with exactly one value: undefined.

+ +

The language also defines a global variable that has the value of undefined, +this variable is also called undefined. But this variable is not a constant, +nor is it a keyword of the language. This means that its value can be easily +overwritten.

+ + + +

Some examples for when the value undefined is returned:

+ +
    +
  • Accessing the (unmodified) global variable undefined.
  • +
  • Implicit returns of functions due to missing return statements.
  • +
  • return statements which do not explicitly return anything.
  • +
  • Lookups of non-existent properties.
  • +
  • Function parameters which do not had any explicit value passed.
  • +
  • Anything that has been set to the value of undefined.
  • +
+ +

Handling Changes to the Value of undefined

+ +

Since the global variable undefined only holds a copy of the actual value of +undefined, assigning a new value to it does not change the value of the +type undefined.

+ +

Still, in order to compare something against the value of undefined it is +necessary to retrieve the value of undefined first.

+ +

In order to protect code against a possible overwritten undefined variable, a +common technique used is to add an additional parameter to an +anonymous wrapper, that gets no argument passed to it.

+ +
var undefined = 123;
+(function(something, foo, undefined) {
+    // undefined in the local scope does 
+    // now again refer to the value
+
+})('Hello World', 42);
+
+ +

Another way to achieve the same effect would be to use a declaration inside the +wrapper.

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

The only difference being here, that this version results in 4 more bytes being +used in case it is minified and there is no other var statement inside the +anonymous wrapper.

+ +

Uses of null

+ +

While undefined in the context of the JavaScript language is mostly used in +the sense of a traditional null, the actual null (both a literal and a type) +is more or less just another data type.

+ +

It is used in some JavaScript internals (like declaring the end of the +prototype chain by setting Foo.prototype = null), but in almost all cases it +can be replaced by undefined.

Automatic Semicolon Insertion

Although JavaScript has C style syntax, it does not enforce the use of +semicolons in the source code, it is possible to omit them.

+ +

But JavaScript is not a semicolon-less language, it in fact needs the +semicolons in order to understand the source code. Therefore the JavaScript +parser automatically inserts them whenever it encounters a parse +error due to a missing semicolon.

+ +
var foo = function() {
+} // parse error, semicolon expected
+test()
+
+ +

Insertion happens, and the parser tries again.

+ +
var foo = function() {
+}; // no error, parser continues
+test()
+
+ +

The automatic insertion of semicolon is considered to be one of biggest +design flaws in the language, as it can change the behavior of code.

+ +

How it Works

+ +

The code below has no semicolons in it, so it is up to the parser to decide where +to insert them.

+ +
(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)
+
+ +

Below is the result of the parser's "guessing" game.

+ +
(function(window, undefined) {
+    function test(options) {
+
+        // Not inserted, lines got merged
+        log('testing!')(options.list || []).forEach(function(i) {
+
+        }); // <- inserted
+
+        options.value.test(
+            'long string to pass here',
+            'and another long string to pass'
+        ); // <- inserted
+
+        return; // <- inserted, breaks the return statement
+        { // treated as a block
+
+            // a label and a single expression statement
+            foo: function() {} 
+        }; // <- inserted
+    }
+    window.test = test; // <- inserted
+
+// The lines got merged again
+})(window)(function(window) {
+    window.someLibrary = {}; // <- inserted
+
+})(window); //<- inserted
+
+ + + +

The parser drastically changed the behavior of the code above, in certain cases +it does the wrong thing.

+ +

Leading Parenthesis

+ +

In case of a leading parenthesis, the parser will not insert a semicolon.

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

This code gets transformed into one line.

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

Chances are very high that log does not return a function; therefore, +the above will yield a TypeError stating that undefined is not a function.

+ +

In Conclusion

+ +

It is highly recommended to never omit semicolons, it is also advocated to +keep braces on the same line with their corresponding statements and to never omit +them for one single-line if / else statements. Both of these measures will +not only improve the consistency of the code, they will also prevent the +JavaScript parser from changing its behavior.

その他

setTimeout and 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); // returns a Number > 0
+
+ +

When setTimeout gets called, it will return the ID of the timeout and schedule +foo to run in approximately one thousand milliseconds in the future. +foo will then get executed exactly once.

+ +

Depending on the timer resolution of the JavaScript engine that is running the +code, as well as the fact that JavaScript is single threaded and other code that +gets executed might block the thread, it is by no means a safe bet that one +will get the exact delay that was specified in the setTimeout call.

+ +

The function that was passed as the first parameter will get called by the +global object, that means, that this inside the called function +refers to that very object.

+ +
function Foo() {
+    this.value = 42;
+    this.method = function() {
+        // this refers to the global object
+        console.log(this.value); // will log undefined
+    };
+    setTimeout(this.method, 500);
+}
+new Foo();
+
+ + + +

Stacking Calls with setInterval

+ +

While setTimeout only runs the function once, setInterval - as the name +suggests - will execute the function every X milliseconds. But its use is +discouraged.

+ +

When code that is being executed blocks the timeout call, setInterval will +still issue more calls to the specified function. This can, especially with small +intervals, result in function calls stacking up.

+ +
function foo(){
+    // something that blocks for 1 second
+}
+setInterval(foo, 100);
+
+ +

In the above code foo will get called once and will then block for one second.

+ +

While foo blocks the code setInterval will still schedule further calls to +it. Now, when foo has finished, there will already be ten further calls to +it waiting for execution.

+ +

Dealing with Possible Blocking Code

+ +

The easiest as well as most controllable solution, is to use setTimeout within +the function itself.

+ +
function foo(){
+    // something that blocks for 1 second
+    setTimeout(foo, 100);
+}
+foo();
+
+ +

Not only does this encapsulate the setTimeout call, but it also prevents the +stacking of calls and it gives additional control.foo itself can now decide +whether it wants to run again or not.

+ +

Manually Clearing Timeouts

+ +

Clearing timeouts and intervals works by passing the respective ID to +clearTimeout or clearInterval, depending which set function was used in +the first place.

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

Clearing all timeouts

+ +

As there is no built-in method for clearing all timeouts and/or intervals, +it is necessary to use brute force in order to achieve this functionality.

+ +
// clear "all" timeouts
+for(var i = 1; i < 1000; i++) {
+    clearTimeout(i);
+}
+
+ +

There might still be timeouts that are unaffected by this arbitrary number; +therefore, is is instead recommended to keep track of all the timeout IDs, so +they can be cleared specifically.

+ +

Hidden use of eval

+ +

setTimeout and setInterval can also take a string as their first parameter. +This feature should never be used, since it internally makes use of eval.

+ + + +
function foo() {
+    // will get called
+}
+
+function bar() {
+    function foo() {
+        // never gets called
+    }
+    setTimeout('foo()', 1000);
+}
+bar();
+
+ +

Since eval is not getting called directly in this case, the string +passed to setTimeout will get executed in the global scope; thus, it will +not use the local variable foo from the scope of bar.

+ +

It is further recommended to not use a string for passing arguments to the +function that will get called by either of the timeout functions.

+ +
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1,2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+    foo(a, b, c);
+}, 1000)
+
+ + + +

In Conclusion

+ +

Never should a string be used as the parameter of setTimeout or +setInterval. It is a clear sign of really bad code, when arguments need +to be supplied to the function that gets called. An anonymous function should +be passed that then takes care of the actual call.

+ +

Further, the use of setInterval should be avoided since its scheduler is not +blocked by executing JavaScript.

\ No newline at end of file From 292e3e01065d6db9117e45f05f1089d372685cc1 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Sat, 2 Jul 2011 23:11:26 +0900 Subject: [PATCH 019/469] Translate Intro to Japanese --- doc/ja/intro/index.md | 40 ++++++++++++++++------------------------ site/ja/index.html | 37 ++++++++++++++----------------------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md index 6ddd7c71..7928eadb 100644 --- a/doc/ja/intro/index.md +++ b/doc/ja/intro/index.md @@ -1,37 +1,29 @@ -## Intro +## 前書き -**JavaScript Garden** is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language. +**JavaScript Garden** はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 -JavaScript Garden does **not** aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent [guide][1] on the Mozilla Developer Network. +JavaScript GardenはJavaScriptを教える事を**目的にしていません**。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkの[ガイド][1] がオススメです。 -## The Authors +## 著者 -This guide is the work of two lovely [Stack Overflow][2] users, [Ivo Wetzel][3] -(Writing) and [Zhang Yi Jiang][4] (Design). +このガイドは2人の愛すべき[Stack Overflow][2]ユーザーである[Ivo Wetzel][3] +(執筆)と[Zhang Yi Jiang][4] (デザイン)によって作られました。 -## Contributors +## 貢献者 - - [Caio Romão][5] (Spelling corrections) - - [Andreas Blixt][6] (Language corrections) + - [Caio Romão][5] (スペル校正) + - [Andreas Blixt][6] (言語校正) -## Hosting +## ホスティング -JavaScript Garden is hosted on GitHub, but [Cramer Development][7] supports us -with a mirror at [JavaScriptGarden.info][8]. +JavaScript GardenはGitHubでホスティングされていますが、[Cramer Development][7]が[JavaScriptGarden.info][8]というミラーサイトを作ってくれています。 -## License +## ライセンス + +JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかレポジトリにプルリクエストを送ってください。 +またStack Overflowチャットの[JavaScript room][12]に私達はいます。 -JavaScript Garden is published under the [MIT license][9] and hosted on -[GitHub][10]. If you find errors or typos please [file an issue][11] or a pull -request on the repository. You can also find us in the [JavaScript room][12] on -Stack Overflow chat. [1]: https://developer.mozilla.org/en/JavaScript/Guide [2]: http://stackoverflow.com/ diff --git a/site/ja/index.html b/site/ja/index.html index 60e5d1c9..ff9f4bb6 100644 --- a/site/ja/index.html +++ b/site/ja/index.html @@ -1,40 +1,31 @@ JavaScript Garden -

イントロ

JavaScript Garden is a growing collection of documentation about the most -quirky parts of the JavaScript programming language. It gives advice to -avoid common mistakes, subtle bugs, as well as performance issues and bad -practices that non-expert JavaScript programmers may encounter on their -endeavours into the depths of the language.

+

前書き

JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

-

JavaScript Garden does not aim to teach you JavaScript. Former knowledge -of the language is strongly recommended in order to understand the topics covered -in this guide. In order to learn the basics of the language, please head over to -the excellent guide on the Mozilla Developer Network.

+

JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

-

The Authors

+

著者

-

This guide is the work of two lovely Stack Overflow users, Ivo Wetzel -(Writing) and Zhang Yi Jiang (Design).

+

このガイドは2人の愛すべきStack OverflowユーザーであるIvo Wetzel +(執筆)とZhang Yi Jiang (デザイン)によって作られました。

-

Contributors

+

貢献者

-

Hosting

+

ホスティング

-

JavaScript Garden is hosted on GitHub, but Cramer Development supports us -with a mirror at JavaScriptGarden.info.

+

JavaScript GardenはGitHubでホスティングされていますが、Cramer DevelopmentJavaScriptGarden.infoというミラーサイトを作ってくれています。

-

License

+

ライセンス

-

JavaScript Garden is published under the MIT license and hosted on -GitHub. If you find errors or typos please file an issue or a pull -request on the repository. You can also find us in the JavaScript room on -Stack Overflow chat.

オブジェクト

Object Usage and Properties

Everything in JavaScript acts like an object, with the only two exceptions being +

JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかレポジトリにプルリクエストを送ってください。 +またStack OverflowチャットのJavaScript roomに私達はいます。

オブジェクト

Object Usage and Properties

Everything in JavaScript acts like an object, with the only two exceptions being null and undefined.

false.toString() // 'false'

From b0ec092a040f7174c286142e261176794084fb37 Mon Sep 17 00:00:00 2001
From: Satoru HIRAKI 
Date: Sat, 2 Jul 2011 23:11:42 +0900
Subject: [PATCH 020/469] Fix intro in index.json

---
 doc/ja/index.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/ja/index.json b/doc/ja/index.json
index 85e25a6b..cea3fb99 100644
--- a/doc/ja/index.json
+++ b/doc/ja/index.json
@@ -4,7 +4,7 @@
     "description": "JavaScriptの奇抜さと欠陥についてのガイドライン",
     "sections": [
         {
-            "title": "イントロ",
+            "title": "前書き",
             "dir": "intro",
             "articles": []
         },

From 843b159e60daf0609f4a2cecb5eb85059e0d542e Mon Sep 17 00:00:00 2001
From: Satoru HIRAKI 
Date: Sun, 3 Jul 2011 00:05:22 +0900
Subject: [PATCH 021/469] fix typo

---
 doc/ja/intro/index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md
index 7928eadb..b2fcfb3c 100644
--- a/doc/ja/intro/index.md
+++ b/doc/ja/intro/index.md
@@ -21,7 +21,7 @@ JavaScript GardenはGitHubでホスティングされていますが、[Cramer D
 
 ## ライセンス
 
-JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかレポジトリにプルリクエストを送ってください。
+JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかリポジトリにプルリクエストを送ってください。
 またStack Overflowチャットの[JavaScript room][12]に私達はいます。
 
 

From f2082d261b5fa806a5cac511ec1d7dd705dfb6d6 Mon Sep 17 00:00:00 2001
From: Satoru HIRAKI 
Date: Sun, 3 Jul 2011 00:07:37 +0900
Subject: [PATCH 022/469] build pl/index.html I don't know why...

---
 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..cfeeb4fa
--- /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 7155c9e223d326fe17a55904d247d551e0d3b18f Mon Sep 17 00:00:00 2001 From: Colinvivy Date: Mon, 4 Jul 2011 00:21:27 +0800 Subject: [PATCH 023/469] modify a wrong word --- doc/zh/other/timeouts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/other/timeouts.md b/doc/zh/other/timeouts.md index 5993e362..e404a6d1 100755 --- a/doc/zh/other/timeouts.md +++ b/doc/zh/other/timeouts.md @@ -35,7 +35,7 @@ `setTimeout` 只会执行回调函数一次,不过 `setInterval` - 正如名字建议的 - 会每隔 `X` 毫秒执行函数一次。 但是却不鼓励使用这个函数。 -当回调函数的执行被阻塞时,`setInterval` 仍然会发布更多的毁掉指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。 +当回调函数的执行被阻塞时,`setInterval` 仍然会发布更多的回调指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。 function foo(){ // 阻塞执行 1 秒 From be35c3e5ad40bad69a0c7471b30e21f13a434bac Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:03:53 +0900 Subject: [PATCH 024/469] translate object/general.md to Japanese --- doc/ja/object/general.md | 74 ++++++++++++++++------------------------ site/ja/index.html | 70 +++++++++++++++---------------------- 2 files changed, 56 insertions(+), 88 deletions(-) diff --git a/doc/ja/object/general.md b/doc/ja/object/general.md index cbad5f11..2838cf91 100644 --- a/doc/ja/object/general.md +++ b/doc/ja/object/general.md @@ -1,7 +1,7 @@ -## Object Usage and Properties +## オブジェクトの使用法とプロパティ -Everything in JavaScript acts like an object, with the only two exceptions being -[`null`](#core.undefined) and [`undefined`](#core.undefined). +JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 +その2つとは[`null`](#core.undefined)と[`undefined`](#core.undefined)です。 false.toString() // 'false' [1, 2, 3].toString(); // '1,2,3' @@ -10,57 +10,46 @@ Everything in JavaScript acts like an object, with the only two exceptions being Foo.bar = 1; Foo.bar; // 1 -A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the *dot -notation* on a number as a floating point literal. +良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットを*ドット記法*として解釈しようとしてしまうからです。 - 2.toString(); // raises SyntaxError + 2.toString(); // シンタックスエラーが発生する -There are a couple of workarounds which can be used in order make number -literals act as objects too. +数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。 - 2..toString(); // the second point is correctly recognized - 2 .toString(); // note the space left to the dot - (2).toString(); // 2 is evaluated first + 2..toString(); // 2つ目のドットが正しく解釈される + 2 .toString(); // ドットの左隣のスペースがポイント + (2).toString(); // 2が一番最初に評価される -### Objects as a Data Type +### オブジェクトはデータタイプ -Objects in JavaScript can also be used as a [*Hashmap*][1], they mainly consist -of named properties mapping to values. +JavaScriptのオブジェクトは[*ハッシュマップ*][1]としても使用されます。これは名前付きのプロパティと値として構成されています。 -Using a object literal - `{}` notation - it is possible to create a -plain object. This new object [inherits](#object.prototype) from `Object.prototype` and -has no [own properties](#object.hasownproperty) defined on it. +オブジェクトリテラル(`{}`記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトは`Object.prototype`から[継承](#object.prototype)され、[own properties](#object.hasownproperty)が何も設定されてない状態になります。 - var foo = {}; // a new empty object + var foo = {}; // 新しい空のオブジェクト - // a new object with a property called 'test' with value 12 + // 12という値の'test'というプロパティを持った新しいオブジェクト var bar = {test: 12}; -### Accessing Properties +### プロパティへのアクセス + +オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。 -The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation. - var foo = {name: 'Kitten'} foo.name; // kitten foo['name']; // kitten - + var get = 'name'; foo[get]; // kitten - - foo.1234; // SyntaxError - foo['1234']; // works -Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error. + foo.1234; // シンタックスエラー + foo['1234']; // 動作する + +どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。 -### Deleting Properties +### プロパティの削除 -The only way to actually remove a property from an object is to use the `delete` -operator; setting the property to `undefined` or `null` only remove the -*value* associated with the property, but not the *key*. +実際にオブジェクトからプロパティを削除する唯一の方法は`delete`演算子を使う事です。プロパティに`undefined`や`null`をセットしても、プロパティ自身ではなく、*キー*に設定された*値*を削除するだけです。 var obj = { bar: 1, @@ -77,23 +66,18 @@ operator; setting the property to `undefined` or `null` only remove the } } -The above outputs both `bar undefined` and `foo null` - only `baz` was -removed and is therefore missing from the output. +上記の例では、`baz`は完全に削除されて出力がされていませんが、それ以外の2つ`bar undefined`と`foo null`はどちらも出力されてしまっています。 -### Notation of Keys +### キーの記法 var test = { 'case': 'I am a keyword so I must be notated as a string', - delete: 'I am a keyword too so me' // raises SyntaxError + delete: 'I am a keyword too so me' // シンタックスエラーが起こる }; -Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, the above will throw -a `SyntaxError` prior to ECMAScript 5. +オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードは`シンタックスエラー`を表示するでしょう。 -This error arises from the fact that `delete` is a *keyword*; therefore, it must be -notated as a *string literal* to ensure that it will be correctly interpreted by -older JavaScript engines. +このエラーは`delete`が*予約語*になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には*文字リテラル*を使って記述する事を推奨します。 [1]: http://en.wikipedia.org/wiki/Hashmap diff --git a/site/ja/index.html b/site/ja/index.html index ff9f4bb6..874fa097 100644 --- a/site/ja/index.html +++ b/site/ja/index.html @@ -1,7 +1,7 @@ JavaScript Garden -

前書き

JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 +

前書き

JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

@@ -24,9 +24,9 @@

ホスティング

ライセンス

-

JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかレポジトリにプルリクエストを送ってください。 -またStack OverflowチャットのJavaScript roomに私達はいます。

オブジェクト

Object Usage and Properties

Everything in JavaScript acts like an object, with the only two exceptions being -null and undefined.

+

JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかリポジトリにプルリクエストを送ってください。 +またStack OverflowチャットのJavaScript roomに私達はいます。

オブジェクト

オブジェクトの使用法とプロパティ

JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 +その2つとはnullundefinedです。

false.toString() // 'false'
 [1, 2, 3].toString(); // '1,2,3'
@@ -36,40 +36,33 @@ 

ライセンス

Foo.bar; // 1
-

A common misconception is that number literals cannot be used as -objects. That is because a flaw in JavaScript's parser tries to parse the dot -notation on a number as a floating point literal.

+

良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットをドット記法として解釈しようとしてしまうからです。

-
2.toString(); // raises SyntaxError
+
2.toString(); // シンタックスエラーが発生する
 
-

There are a couple of workarounds which can be used in order make number -literals act as objects too.

+

数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。

-
2..toString(); // the second point is correctly recognized
-2 .toString(); // note the space left to the dot
-(2).toString(); // 2 is evaluated first
+
2..toString(); // 2つ目のドットが正しく解釈される
+2 .toString(); // ドットの左隣のスペースがポイント
+(2).toString(); // 2が一番最初に評価される
 
-

Objects as a Data Type

+

オブジェクトはデータタイプ

-

Objects in JavaScript can also be used as a Hashmap, they mainly consist -of named properties mapping to values.

+

JavaScriptのオブジェクトはハッシュマップとしても使用されます。これは名前付きのプロパティと値として構成されています。

-

Using a object literal - {} notation - it is possible to create a -plain object. This new object inherits from Object.prototype and -has no own properties defined on it.

+

オブジェクトリテラル({}記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトはObject.prototypeから継承され、own propertiesが何も設定されてない状態になります。

-
var foo = {}; // a new empty object
+
var foo = {}; // 新しい空のオブジェクト
 
-// a new object with a property called 'test' with value 12
+// 12という値の'test'というプロパティを持った新しいオブジェクト
 var bar = {test: 12}; 
 
-

Accessing Properties

+

プロパティへのアクセス

-

The properties of an object can be accessed in two ways, via either the dot -notation, or the square bracket notation.

+

オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。

var foo = {name: 'Kitten'}
 foo.name; // kitten
@@ -78,19 +71,15 @@ 

ライセンス

var get = 'name'; foo[get]; // kitten -foo.1234; // SyntaxError -foo['1234']; // works +foo.1234; // シンタックスエラー +foo['1234']; // 動作する
-

Both notations are identical in their workings, with the only difference being that -the square bracket notation allows for dynamic setting of properties, as well as -the use of property names that would otherwise lead to a syntax error.

+

どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。

-

Deleting Properties

+

プロパティの削除

-

The only way to actually remove a property from an object is to use the delete -operator; setting the property to undefined or null only remove the -value associated with the property, but not the key.

+

実際にオブジェクトからプロパティを削除する唯一の方法はdelete演算子を使う事です。プロパティにundefinednullをセットしても、プロパティ自身ではなく、キーに設定されたを削除するだけです。

var obj = {
     bar: 1,
@@ -108,24 +97,19 @@ 

ライセンス

}
-

The above outputs both bar undefined and foo null - only baz was -removed and is therefore missing from the output.

+

上記の例では、bazは完全に削除されて出力がされていませんが、それ以外の2つbar undefinedfoo nullはどちらも出力されてしまっています。

-

Notation of Keys

+

キーの記法

var test = {
     'case': 'I am a keyword so I must be notated as a string',
-    delete: 'I am a keyword too so me' // raises SyntaxError
+    delete: 'I am a keyword too so me' // シンタックスエラーが起こる
 };
 
-

Object properties can be both notated as plain characters and as strings. Due to -another mis-design in JavaScript's parser, the above will throw -a SyntaxError prior to ECMAScript 5.

+

オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードはシンタックスエラーを表示するでしょう。

-

This error arises from the fact that delete is a keyword; therefore, it must be -notated as a string literal to ensure that it will be correctly interpreted by -older JavaScript engines.

The Prototype

JavaScript does not feature a classical inheritance model, instead it uses a +

このエラーはdelete予約語になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には文字リテラルを使って記述する事を推奨します。

The Prototype

JavaScript does not feature a classical inheritance model, instead it uses a prototypal one.

While this is often considered to be one of JavaScript's weaknesses, the From 4f3c32ff9e1a5e0ac0d3ece07610ea1d0f8513ea Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:16:12 +0900 Subject: [PATCH 025/469] add ja and pl --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fecb262f..a602d945 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,7 @@ /site/en /site/fi /site/tr +/site/pl +/site/ja *.md~ *.src.md From 5e4532f0f6adc593ecf4795bfdbe003b8a54a5d8 Mon Sep 17 00:00:00 2001 From: Satoru HIRAKI Date: Mon, 4 Jul 2011 03:19:06 +0900 Subject: [PATCH 026/469] delate ja and pl from repository --- site/ja/index.html | 1853 ------------------------------------------ site/pl/index.html | 1914 -------------------------------------------- 2 files changed, 3767 deletions(-) delete mode 100644 site/ja/index.html delete mode 100644 site/pl/index.html diff --git a/site/ja/index.html b/site/ja/index.html deleted file mode 100644 index 874fa097..00000000 --- a/site/ja/index.html +++ /dev/null @@ -1,1853 +0,0 @@ -JavaScript Garden -

前書き

JavaScript Garden はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 -このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。

- -

JavaScript GardenはJavaScriptを教える事を目的にしていません。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkのガイド がオススメです。

- -

著者

- -

このガイドは2人の愛すべきStack OverflowユーザーであるIvo Wetzel -(執筆)とZhang Yi Jiang (デザイン)によって作られました。

- -

貢献者

- - - -

ホスティング

- -

JavaScript GardenはGitHubでホスティングされていますが、Cramer DevelopmentJavaScriptGarden.infoというミラーサイトを作ってくれています。

- -

ライセンス

- -

JavaScript GardenはMIT licenseの下で公開されており、GitHubでホスティングされています。もしもエラーやtypoを見つけたらfile an issueに登録するかリポジトリにプルリクエストを送ってください。 -またStack OverflowチャットのJavaScript roomに私達はいます。

オブジェクト

オブジェクトの使用法とプロパティ

JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 -その2つとはnullundefinedです。

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

良くありがちな誤解として、数値リテラルがオブジェクトとして使用できないというものがあります。この理由としては、JavaScriptパーサーが浮動小数点のドットをドット記法として解釈しようとしてしまうからです。

- -
2.toString(); // シンタックスエラーが発生する
-
- -

数値リテラルをオブジェクトとして使用する為の回避策がいくつかあります。

- -
2..toString(); // 2つ目のドットが正しく解釈される
-2 .toString(); // ドットの左隣のスペースがポイント
-(2).toString(); // 2が一番最初に評価される
-
- -

オブジェクトはデータタイプ

- -

JavaScriptのオブジェクトはハッシュマップとしても使用されます。これは名前付きのプロパティと値として構成されています。

- -

オブジェクトリテラル({}記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトはObject.prototypeから継承され、own propertiesが何も設定されてない状態になります。

- -
var foo = {}; // 新しい空のオブジェクト
-
-// 12という値の'test'というプロパティを持った新しいオブジェクト
-var bar = {test: 12}; 
-
- -

プロパティへのアクセス

- -

オブジェクトのプロパティには2通りのアクセス方法があります。1つはドット記法によるアクセス、もう1つはブラケット記法です。

- -
var foo = {name: 'Kitten'}
-foo.name; // kitten
-foo['name']; // kitten
-
-var get = 'name';
-foo[get]; // kitten
-
-foo.1234; // シンタックスエラー
-foo['1234']; // 動作する
-
- -

どちらの記法も働きとしての違いは無いですが、唯一の違いとしてブラケット記法は通常のプロパティ名と同様に動的にプロパティを設定する事ができます。これ以外で動的にプロパティを設定しようとするとシンタックスエラーになります。

- -

プロパティの削除

- -

実際にオブジェクトからプロパティを削除する唯一の方法はdelete演算子を使う事です。プロパティにundefinednullをセットしても、プロパティ自身ではなく、キーに設定されたを削除するだけです。

- -
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]);
-    }
-}
-
- -

上記の例では、bazは完全に削除されて出力がされていませんが、それ以外の2つbar undefinedfoo nullはどちらも出力されてしまっています。

- -

キーの記法

- -
var test = {
-    'case': 'I am a keyword so I must be notated as a string',
-    delete: 'I am a keyword too so me' // シンタックスエラーが起こる
-};
-
- -

オブジェクトのプロパティは普通の文字か文字列として記述する事が出来ます。JavaScriptパーサーの設計ミスが原因ですが、ECMAScript5以前では上記のコードはシンタックスエラーを表示するでしょう。

- -

このエラーはdelete予約語になっているのが原因なので、古いJavaScriptエンジンに正しく解釈させる為には文字リテラルを使って記述する事を推奨します。

The Prototype

JavaScript does not feature a classical inheritance model, instead it uses a -prototypal one.

- -

While this is often considered to be one of JavaScript's weaknesses, the -prototypal inheritance model is in fact more powerful than the classic model. -It is for example fairly trivial to build a classic model on top of it, while the -other way around is a far more difficult task.

- -

Due to the fact that JavaScript is basically the only widely used language that -features prototypal inheritance, it takes some time to adjust to the -differences between the two models.

- -

The first major difference is that inheritance in JavaScript is done by using so -called prototype chains.

- - - -
function Foo() {
-    this.value = 42;
-}
-Foo.prototype = {
-    method: function() {}
-};
-
-function Bar() {}
-
-// Set Bar's prototype to a new instance of Foo
-Bar.prototype = new Foo();
-Bar.prototype.foo = 'Hello World';
-
-// Make sure to list Bar as the actual constructor
-Bar.prototype.constructor = Bar;
-
-var test = new Bar() // create a new bar instance
-
-// The resulting prototype chain
-test [instance of Bar]
-    Bar.prototype [instance of Foo] 
-        { foo: 'Hello World' }
-        Foo.prototype
-            { method: ... }
-            Object.prototype
-                { toString: ... /* etc. */ }
-
- -

In the above, the object test will inherit from both Bar.prototype and -Foo.prototype; hence, it will have access to the function method that was -defined on Foo. It will also have access to the property value of the -one Foo instance that is its prototype. It is important to note that new -Bar() does not create a new Foo instance, but reuses the one assigned to -its prototype; thus, all Bar instances will share the same value property.

- - - -

Property Lookup

- -

When accessing the properties of an object, JavaScript will traverse the -prototype chain upwards until it finds a property with the requested name.

- -

When it reaches the top of the chain - namely Object.prototype - and still -hasn't found the specified property, it will return the value -undefined instead.

- -

The Prototype Property

- -

While the prototype property is used by the language to build the prototype -chains, it is still possible to assign any given value to it. Although -primitives will simply get ignored when assigned as a prototype.

- -
function Foo() {}
-Foo.prototype = 1; // no effect
-
- -

Assigning objects, as shown in the example above, will work, and allows for dynamic -creation of prototype chains.

- -

Performance

- -

The lookup time for properties that are high up on the prototype chain can have a -negative impact on performance critical sections of code. Additionally, trying to -access non-existent properties will always traverse the full prototype chain.

- -

Also, when iterating over the properties of an object -every property that is on the prototype chain will get enumerated.

- -

Extension of Native Prototypes

- -

One mis-feature that is often used is to extend Object.prototype or one of the -other built in prototypes.

- -

This technique is called monkey patching and breaks encapsulation. While -used by widely spread frameworks such as Prototype, there is still no good -reason for cluttering built-in types with additional non-standard functionality.

- -

The only good reason for extending a built-in prototype is to backport -the features of newer JavaScript engines; for example, -Array.forEach.

- -

In Conclusion

- -

It is a must to understand the prototypal inheritance model completely -before writing complex code which makes use of it. Also, watch the length of -the prototype chains and break them up if necessary to avoid possible -performance issues. Further, the native prototypes should never be extended -unless it is for the sake of compatibility with newer JavaScript features.

hasOwnProperty

In order to check whether a object has a property defined on itself and not -somewhere on its prototype chain, it is necessary to use the -hasOwnProperty method which all objects inherit from Object.prototype.

- - - -

hasOwnProperty is the only thing in JavaScript which deals with properties and -does not traverse the prototype chain.

- -
// Poisoning 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
-
- -

Only hasOwnProperty will give the correct and expected result, this is -essential when iterating over the properties of any object. There is no other -way to exclude properties that are not defined on the object itself, but -somewhere on its prototype chain.

- -

hasOwnProperty as a Property

- -

JavaScript does not protect the property name hasOwnProperty; thus, if the -possibility exists that an object might have a property with this name, it is -necessary to use an external hasOwnProperty in order to get correct results.

- -
var foo = {
-    hasOwnProperty: function() {
-        return false;
-    },
-    bar: 'Here be dragons'
-};
-
-foo.hasOwnProperty('bar'); // always returns false
-
-// Use another Object's hasOwnProperty and call it with 'this' set to foo
-({}).hasOwnProperty.call(foo, 'bar'); // true
-
- -

In Conclusion

- -

When checking for the existence of a property on a object, hasOwnProperty is -the only method of doing so. It is also recommended to make hasOwnProperty -part of every for in loop, this will avoid errors from -extended native prototypes.

The for in Loop

Just like the in operator, the for in loop also traverses the prototype -chain when iterating over the properties of an object.

- - - -
// Poisoning Object.prototype
-Object.prototype.bar = 1;
-
-var foo = {moo: 2};
-for(var i in foo) {
-    console.log(i); // prints both bar and moo
-}
-
- -

Since it is not possible to change the behavior of the for in loop itself, it -is necessary to filter out the unwanted properties inside the loop body , -this is done by using the hasOwnProperty method of -Object.prototype.

- - - -

Using hasOwnProperty for Filtering

- -
// still the foo from above
-for(var i in foo) {
-    if (foo.hasOwnProperty(i)) {
-        console.log(i);
-    }
-}
-
- -

This version is the only correct one to use. Due to the use of hasOwnProperty it -will only print out moo. When hasOwnProperty is left out, the code is -prone to errors in cases where the native prototypes - e.g. Object.prototype - -have been extended.

- -

One widely used framework which does this is Prototype. When this -framework is included, for in loops that do not use hasOwnProperty are -guaranteed to break.

- -

In Conclusion

- -

It is recommended to always use hasOwnProperty. Never should any -assumptions be made about the environment the code is running in, or whether the -native prototypes have been extended or not.

関数

Function Declarations and Expressions

Functions in JavaScript are first class objects. That means they can be -passed around like any other value. One common use of this feature is to pass -an anonymous function as a callback to another, possibly asynchronous function.

- -

The function Declaration

- -
function foo() {}
-
- -

The above function gets hoisted before the execution of the -program starts; thus, it is available everywhere in the scope it was defined -in, even if called before the actual definition in the source.

- -
foo(); // Works because foo was created before this code runs
-function foo() {}
-
- -

The function Expression

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

This example assigns the unnamed and anonymous function to the variable foo.

- -
foo; // 'undefined'
-foo(); // this raises a TypeError
-var foo = function() {};
-
- -

Due to the fact that var is a declaration, that hoists the variable name foo -before the actual execution of the code starts, foo is already defined when -the script gets executed.

- -

But since assignments only happen at runtime, the value of foo will default -to undefined before the corresponding code is executed.

- -

Named Function Expression

- -

Another special case is the assignment of named functions.

- -
var foo = function bar() {
-    bar(); // Works
-}
-bar(); // ReferenceError
-
- -

Here bar is not available in the outer scope, since the function only gets -assigned to foo; however, inside of bar it is available. This is due to -how name resolution in JavaScript works, the name of the -function is always made available in the local scope of the function itself.

How this Works

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.

- -

The Global Scope

- -
this;
-
- -

When using this in global scope, it will simply refer to the global object.

- -

Calling a Function

- -
foo();
-
- -

Here this will again refer to the global object.

- - - -

Calling a Method

- -
test.foo(); 
-
- -

In this example this will refer to test.

- -

Calling a Constructor

- -
new foo(); 
-
- -

A function call that is preceded by the new keyword acts as -a constructor. Inside the function this will refer -to a newly created Object.

- -

Explicit Setting of this

- -
function foo(a, b, c) {}
-
-var bar = {};
-foo.apply(bar, [1, 2, 3]); // array will expand to the below
-foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
-
- -

When using the call or apply methods of Function.prototype, the value of -this inside the called function gets explicitly set to the first argument -of the corresponding function call.

- -

As a result, the above example the method case does not apply, and this -inside of foo will be set to bar.

- - - -

Common Pitfalls

- -

While most of these cases make sense, the first one is to be considered another -mis-design of the language, as it never has any practical use.

- -
Foo.method = function() {
-    function test() {
-        // this is set to the global object
-    }
-    test();
-}
-
- -

A common misconception is that this inside of test refers to Foo, while in -fact it does not.

- -

In order to gain access to Foo from within test it is necessary to create a -local variable inside of method which refers to Foo.

- -
Foo.method = function() {
-    var that = this;
-    function test() {
-        // Use that instead of this here
-    }
-    test();
-}
-
- -

that is just a normal variable name, but it is commonly used for the reference to an -outer this. In combination with closures, it can also -be used to pass this values around.

- -

Assigning Methods

- -

Another thing that does not work in JavaScript is function aliasing, that is, -assigning a method to a variable.

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

Due to the first case test now acts like a plain function call; therefore, -this inside it will no longer refer to someObject.

- -

While the late binding of this might seem like a bad idea at first, it is in -fact what makes prototypal inheritance work.

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

When method gets called on a instance of Bar, this will now refer to that -very instance.

Closures and References

One of JavaScript's most powerful features is the availability of closures, -this means that scopes always keep access to the outer scope they were -defined in. Since the only scoping that JavaScript has is -function scope, all functions, by default, act as closures.

- -

Emulating private variables

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

Here, Counter returns two closures. The function increment as well as -the function get. Both of these functions keep a reference to the scope of -Counter and, therefore, always keep access to the count variable that was -defined in that very scope.

- -

Why Private Variables Work

- -

Since it is not possible to reference or assign scopes in JavaScript, there is -no way of accessing the variable count from the outside. The only way to -interact with it is via the two closures.

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

The above code will not change the variable count in the scope of Counter, -since foo.hack was not defined in that scope. It will instead create - or -override - the global variable count.

- -

Closures Inside Loops

- -

One often made mistake is to use closures inside of loops, as if they were -copying the value of the loops index variable.

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

The above will not output the numbers 0 through 9, but will simply print -the number 10 ten times.

- -

The anonymous function keeps a reference to i and at the time -console.log gets called, the for loop has already finished and the value of -i as been set to 10.

- -

In order to get the desired behavior, it is necessary to create a copy of -the value of i.

- -

Avoiding the Reference Problem

- -

In order to copy the value of the loop's index variable, it is best to use an -anonymous wrapper.

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

The anonymous outer function gets called immediately with i as its first -argument and will receive a copy of the value of i as its parameter e.

- -

The anonymous function that gets passed to setTimeout now has a reference to -e, whose value does not get changed by the loop.

- -

There is another possible way of achieving this; that is to return a function -from the anonymous wrapper, that will then have the same behavior as the code -above.

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

The arguments Object

Every function scope in JavaScript can access the special variable arguments. -This variable holds a list of all the arguments that were passed to the function.

- - - -

The arguments object is not an Array. While it has some of the -semantics of an array - namely the length property - it does not inherit from -Array.prototype and is in fact an Object.

- -

Due to this, it is not possible to use standard array methods like push, -pop or slice on arguments. While iteration with a plain for loop works -just fine, it is necessary to convert it to a real Array in order to use the -standard Array methods on it.

- -

Converting to an Array

- -

The code below will return a new Array containing all the elements of the -arguments object.

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

This conversion is slow, it is not recommended to use it in performance -critical sections of code.

- -

Passing Arguments

- -

The following is the recommended way of passing arguments from one function to -another.

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

Another trick is to use both call and apply together to create fast, unbound -wrappers.

- -
function Foo() {}
-
-Foo.prototype.method = function(a, b, c) {
-    console.log(this, a, b, c);
-};
-
-// Create an unbound version of "method" 
-// It takes the parameters: this, arg1, arg2...argN
-Foo.method = function() {
-
-    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
-    Function.call.apply(Foo.prototype.method, arguments);
-};
-
- -

Formal Parameters and Arguments Indices

- -

The arguments object creates getter and setter functions for both its -properties as well as the function's formal parameters.

- -

As a result, changing the value of a formal parameter will also change the value -of the corresponding property on the arguments object, and the other way around.

- -
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);
-
- -

Performance Myths and Truths

- -

The arguments object is always created with the only two exceptions being the -cases where it is declared as a name inside of a function or one of its formal -parameters. It does not matter whether it is used or not.

- -

Both getters and setters are always created; thus, using it has nearly -no performance impact at all, especially not in real world code where there is -more than a simple access to the arguments object's properties.

- - - -

However, there is one case which will drastically reduce the performance in -modern JavaScript engines. That case is the use of arguments.callee.

- -
function foo() {
-    arguments.callee; // do something with this function object
-    arguments.callee.caller; // and the calling function object
-}
-
-function bigLoop() {
-    for(var i = 0; i < 100000; i++) {
-        foo(); // Would normally be inlined...
-    }
-}
-
- -

In the above code, foo can no longer be a subject to inlining since it -needs to know about both itself and its caller. This not only defeats possible -performance gains that would arise from inlining, it also breaks encapsulation -since the function may now be dependent on a specific calling context.

- -

It is highly recommended to never make use of arguments.callee or any of -its properties.

- -

Constructors

Constructors in JavaScript are yet again different from many other languages. Any -function call that is preceded by the new keyword acts as a constructor.

- -

Inside the constructor - the called function - the value of this refers to a -newly created Object. The prototype of this new -object is set to the prototype of the function object that was invoked as the -constructor.

- -

If the function that was called has no explicit return statement, then it -implicitly returns the value of this - the new object.

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

The above calls Foo as constructor and sets the prototype of the newly -created object to Foo.prototype.

- -

In case of an explicit return statement the function returns the value -specified that statement, but only if the return value is an Object.

- -
function Bar() {
-    return 2;
-}
-new Bar(); // a new object
-
-function Test() {
-    this.value = 2;
-
-    return {
-        foo: 1
-    };
-}
-new Test(); // the returned object
-
- -

When the new keyword is omitted, the function will not return a new object.

- -
function Foo() {
-    this.bla = 1; // gets set on the global object
-}
-Foo(); // undefined
-
- -

While the above example might still appear to work in some cases, due to the -workings of this in JavaScript, it will use the -global object as the value of this.

- -

Factories

- -

In order to be able to omit the new keyword, the constructor function has to -explicitly return a value.

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

Both calls to Bar return the exact same thing, a newly create object which -has a property called method on it, that is a -Closure.

- -

It is also to note that the call new Bar() does not affect the prototype -of the returned object. While the prototype will be set on the newly created -object, Bar never returns that new object.

- -

In the above example, there is no functional difference between using and -not using the new keyword.

- -

Creating New Objects via Factories

- -

An often made recommendation is to not use new since forgetting its use -may lead to bugs.

- -

In order to create new object, one should rather use a factory and construct a -new object inside of that factory.

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

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. It uses more memory since the created objects do not share the methods -on a prototype.
  2. -
  3. In order to inherit the factory needs to copy all the methods from another -object or put that object on the prototype of the new object.
  4. -
  5. Dropping the prototype chain just because of a left out new keyword -somehow goes against the spirit of the language.
  6. -
- -

In Conclusion

- -

While omitting the new keyword might lead to bugs, it is certainly not a -reason to drop the use of prototypes altogether. In the end it comes down to -which solution is better suited for the needs of the application, it is -especially important to choose a specific style of object creation and stick -with it.

Scopes and Namespaces

Although JavaScript deals fine with the syntax of two matching curly -braces for blocks, it does not support block scope; hence, all that is left -is in the language is function scope.

- -
function test() { // a scope
-    for(var i = 0; i < 10; i++) { // not a scope
-        // count
-    }
-    console.log(i); // 10
-}
-
- - - -

There are also no distinct namespaces in JavaScript, that means that everything -gets defined in one globally shared namespace.

- -

Each time a variable is referenced, JavaScript will traverse upwards through all -the scopes until it finds it. In the case that it reaches the global scope and -still has not found the requested name, it will raise a ReferenceError.

- -

The Bane of Global Variables

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

The above two scripts do not have the same effect. Script A defines a -variable called foo in the global scope and script B defines a foo in the -current scope.

- -

Again, that is not at all the same effect, not using var can have major -implications.

- -
// global scope
-var foo = 42;
-function test() {
-    // local scope
-    foo = 21;
-}
-test();
-foo; // 21
-
- -

Leaving out the var statement inside the function test will override the -value of foo. While this might not seem like a big deal at first, having -thousands of lines of JavaScript and not using var will introduce horrible and -hard to track down bugs.

- -
// global scope
-var items = [/* some list */];
-for(var i = 0; i < 10; i++) {
-    subLoop();
-}
-
-function subLoop() {
-    // scope of subLoop
-    for(i = 0; i < 10; i++) { // missing var statement
-        // do amazing stuff!
-    }
-}
-
- -

The outer loop will terminate after the first call to subLoop, since subLoop -overwrites the global value of i. Using a var for the second for loop would -have easily avoided this error. The var statement should never be left out -unless the desired effect is to affect the outer scope.

- -

Local Variables

- -

The only source for local variables in JavaScript are -function parameters and variables that were declared via the -var statement.

- -
// global scope
-var foo = 1;
-var bar = 2;
-var i = 2;
-
-function test(i) {
-    // local scope of the function test
-    i = 5;
-
-    var foo = 3;
-    bar = 4;
-}
-test(10);
-
- -

While foo and i are local variables inside the scope of the function test, -the assignment of bar will override the global variable with the same name.

- -

Hoisting

- -

JavaScript hoists declarations. This means that both var statements and -function declarations will be moved to the top of their enclosing scope.

- -
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];
-    }
-}
-
- -

The above code gets transformed before any execution is started. JavaScript moves -the var statements as well as the function declarations to the top of the -nearest surrounding scope.

- -
// var statements got moved here
-var bar, someValue; // default to 'undefined'
-
-// the function declartion got moved up too
-function test(data) {
-    var goo, i, e; // missing block scope moves these here
-    if (false) {
-        goo = 1;
-
-    } else {
-        goo = 2;
-    }
-    for(i = 0; i < 100; i++) {
-        e = data[i];
-    }
-}
-
-bar(); // fails with a TypeError since bar is still 'undefined'
-someValue = 42; // assignments are not affected by hoisting
-bar = function() {};
-
-test();
-
- -

Missing block scoping will not only move var statements out of loops and -their bodies, it will also make the results of certain if constructs -non-intuitive.

- -

In the original code the if statement seemed to modify the global -variable goo, while actually it modifies the local variable - after hoisting -has been applied.

- -

Without the knowledge about hoisting, below code might seem to raise a -ReferenceError.

- -
// check whether SomeImportantThing has been initiliazed
-if (!SomeImportantThing) {
-    var SomeImportantThing = {};
-}
-
- -

But of course, the above works due to the fact that the var statement is being -moved to the top of the global scope.

- -
var SomeImportantThing;
-
-// other code might initiliaze SomeImportantThing here, or not
-
-// make sure it's there
-if (!SomeImportantThing) {
-    SomeImportantThing = {};
-}
-
- -

Name Resolution Order

- -

All scopes in JavaScript, including the global scope, have the special name -this defined in them, which refers to the current object.

- -

Function scopes also have the name arguments defined in -them which contains the arguments that were passed to a function.

- -

For example, when trying to access a variable named foo inside the scope of a -function, JavaScript will lookup the name in the following order:

- -
    -
  1. In case there is a var foo statement in the current scope use that.
  2. -
  3. If one of the function parameters is named foo use that.
  4. -
  5. If the function itself is called foo use that.
  6. -
  7. Go to the next outer scope and start with #1 again.
  8. -
- - - -

Namespaces

- -

A common problem of having only one global namespace is the likeliness of running -into problems where variable names clash. In JavaScript, this problem can -easily be avoided with the help of anonymous wrappers.

- -
(function() {
-    // a self contained "namespace"
-
-    window.foo = function() {
-        // an exposed closure
-    };
-
-})(); // execute the function immediately
-
- -

Unnamed functions are considered expressions; so in order to -being callable, they must first be evaluated.

- -
( // evaluate the function inside the paranthesis
-function() {}
-) // and return the function object
-() // call the result of the evaluation
-
- -

There are other ways for evaluating and calling the function expression; which, -while different in syntax, do behave the exact same way.

- -
// Two other ways
-+function(){}();
-(function(){}());
-
- -

In Conclusion

- -

It is recommended to always use an anonymous wrapper for encapsulating code in -its own namespace. This does not only protect code against name clashes, it -also allows for better modularization of programs.

- -

Additionally, the use of global variables is considered bad practice. Any -use of them indicates badly written code that is prone to errors and hard to maintain.

配列

Array Iteration and Properties

Although arrays in JavaScript are objects, there are no good reasons to use -the for in loop in for iteration on them. In fact there -are a number of good reasons against the use of for in on arrays.

- - - -

Since the for in loop enumerates all the properties that are on the prototype -chain and the only way to exclude those properties is to use -hasOwnProperty, it is already up to twenty times -slower than a normal for loop.

- -

Iteration

- -

In order to achieve the best performance when iterating over arrays, it is best -to use the classic for loop.

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

There is one extra catch in the above example, that is the caching of the -length of the array via l = list.length.

- -

Although the length property is defined on the array itself, there is still an -overhead for doing the lookup on each iteration of the loop. And while recent -JavaScript engines may apply optimization in this case, there is no way of -telling whether the code will run on one of these newer engines or not.

- -

In fact, leaving out the caching may result in the loop being only half as -fast as with the cached length.

- -

The length Property

- -

While the getter of the length property simply returns the number of -elements that are contained in the array, the setter can be used to -truncate the array.

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

Assigning a smaller length does truncate the array, but increasing the length -does not have any effect on the array.

- -

In Conclusion

- -

For the best performance it is recommended to always use the plain for loop -and cache the length property. The use of for in on an array is a sign of -badly written code that is prone to bugs and bad performance.

The Array Constructor

Since the Array constructor is ambiguous in how it deals with its parameters, -it is highly recommended to always use the array literals - [] notation - -when creating new arrays.

- -
[1, 2, 3]; // Result: [1, 2, 3]
-new Array(1, 2, 3); // Result: [1, 2, 3]
-
-[3]; // Result: [3]
-new Array(3); // Result: []
-new Array('3') // Result: ['3']
-
- -

In cases when there is only one argument passed to the Array constructor, -and that argument is a Number, the constructor will return a new sparse -array with the length property set to the value of the argument. It should be -noted that only the length property of the new array will be set this way, -the actual indexes of the array will not be initialized.

- -
var arr = new Array(3);
-arr[1]; // undefined
-1 in arr; // false, the index was not set
-
- -

The behavior of being able to set the length of the array upfront only comes in -handy in a few cases, like repeating a string, in which it avoids the use of a -for loop code.

- -
new Array(count + 1).join(stringToRepeat);
-
- -

In Conclusion

- -

The use of the Array constructor should be avoided as much as possible. -Literals are definitely preferred. They are shorter and have a clearer syntax; -therefore, they also increase the readability of the code.

Equality and Comparisons

JavaScript has two different ways of comparing the values of objects for equality.

- -

The Equality Operator

- -

The equality operator consists of two equal signs: ==

- -

JavaScript features weak typing. This means that the equality operator -coerces types in order to compare them.

- -
""           ==   "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
-
- -

The above table shows the results of the type coercion and it is the main reason -why the use of == is widely regarded as bad practice, it introduces hard to -track down bugs due to its complicated conversion rules.

- -

Additionally there is also a performance impact when type coercion is in play; -for example, a string has to be converted to a number before it can be compared -to another number.

- -

The Strict Equality Operator

- -

The strict equality operator consists of three equal signs: ===

- -

It works exactly like the normal equality operator, except that strict equality -operator does not perform type coercion between its operands.

- -
""           ===   "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
-
- -

The above results are a lot clearer and allow for early breakage of code. This -hardens code to a certain degree and also gives performance improvements in case -the operands are of different types.

- -

Comparing Objects

- -

While both == and === are stated as equality operators, they behave -different when at least one of their operands happens to be an Object.

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

Here both operators compare for identity and not equality; that is, they -will compare for the same instance of the object, much like is in Python -and pointer comparison in C.

- -

In Conclusion

- -

It is highly recommended to only use the strict equality operator. In cases -where types need to be coerced, it should be done explicitly -and not left to the language's complicated coercion rules.

The typeof Operator

The typeof operator (together with -instanceof) is probably the biggest -design flaw of JavaScript, as it is near of being completely broken.

- -

Although instanceof still has its limited uses, typeof really has only one -practical use case, which does not happen to be checking the type of an -object.

- - - -

The JavaScript Type Table

- -
Value               Class      Type
--------------------------------------
-"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 in Nitro/V8)
-new RegExp("meow")  RegExp     object (function in Nitro/V8)
-{}                  Object     object
-new Object()        Object     object
-
- -

In the above table Type refers to the value, that the typeof operator returns. -As can be clearly seen, this value is anything but consistent.

- -

The Class refers to the value of the internal [[Class]] property of an object.

- - - -

In order to retrieve the value of [[Class]] one has to make use of the -toString method of Object.prototype.

- -

The Class of an Object

- -

The specification gives exactly one way of accessing the [[Class]] value, -with the use of 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
-
- -

In the above example, Object.prototype.toString gets called with the value of -this being set to the object whose [[Class]] value should be -retrieved.

- - - -

Testing for Undefined Variables

- -
typeof foo !== 'undefined'
-
- -

The above will check whether foo was actually declared or not; just -referencing it would result in a ReferenceError. This is the only thing -typeof is actually useful for.

- -

In Conclusion

- -

In order to check the type of an object, it is highly recommended to use -Object.prototype.toString; as this is the only reliable way of doing so. -As shown in the above type table, some return values of typeof are not defined -in the specification; thus, they can differ across various implementations.

- -

Unless checking whether a variable is defined, typeof should be avoided at -all costs.

The instanceof Operator

The instanceof operator compares the constructors of its two operands. It is -only useful when comparing custom made objects. Used on built-in types, it is -nearly as useless as the typeof operator.

- -

Comparing Custom Objects

- -
function Foo() {}
-function Bar() {}
-Bar.prototype = new Foo();
-
-new Bar() instanceof Bar; // true
-new Bar() instanceof Foo; // true
-
-// This just sets Bar.prototype to the function object Foo
-// But not to an actual instance of Foo
-Bar.prototype = Foo;
-new Bar() instanceof Foo; // false
-
- -

Using instanceof with Native Types

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

One important thing to note here is, that instanceof does not work on objects -that origin from different JavaScript contexts (e.g. different documents -in a web browser), since their constructors will not be the exact same object.

- -

In Conclusion

- -

The instanceof operator should only be used when dealing with custom made -objects that origin from the same JavaScript context. Just like the -typeof operator, every other use of it should be avoided.

Type Casting

JavaScript is a weakly typed language, so it will apply type coercion -wherever possible.

- -
// These are true
-new Number(10) == 10; // Number.toString() is converted
-                      // back to a number
-
-10 == '10';           // Strings gets converted to Number
-10 == '+10 ';         // More string madness
-10 == '010';          // And more 
-isNaN(null) == false; // null converts to 0
-                      // which of course is not NaN
-
-// These are false
-10 == 010;
-10 == '-10';
-
- - - -

In order to avoid the above, use of the strict equal operator -is highly recommended. Although this avoids a lot of common pitfalls, there -are still many further issues that arise from JavaScript's weak typing system.

- -

Constructors of Built-In Types

- -

The constructors of the built in types like Number and String behave -differently when being used with the new keyword and without it.

- -
new Number(10) === 10;     // False, Object and Number
-Number(10) === 10;         // True, Number and Number
-new Number(10) + 0 === 10; // True, due to implicit conversion
-
- -

Using a built-in type like Number as a constructor will create a new Number -object, but leaving out the new keyword will make the Number function behave -like a converter.

- -

In addition, having literals or non-object values in there will result in even -more type coercion.

- -

The best option is to cast to one of the three possible types explicitly.

- -

Casting to a String

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

By prepending a empty string a value can easily be casted to a string.

- -

Casting to a Number

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

Using the unary plus operator it is possible to cast to a number.

- -

Casting to a Boolean

- -

By using the not operator twice, a value can be converted a boolean.

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

コア

Why Not to Use eval

The eval function will execute a string of JavaScript code in the local scope.

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

But eval only executes in local scope when it is being called directly and -the name of the called function is actually eval.

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

The use of eval should be avoided at all costs. 99.9% of its "uses" can be -achieved without it.

- -

eval in Disguise

- -

The timeout functions setTimeout and setInterval can both -take a string as their first argument. This string will always get executed -in the global scope since eval is not being called directly in that case.

- -

Security Issues

- -

eval also is a security problem as it executes any code given to it, -it should never be used with strings of unknown or untrusted origins.

- -

In Conclusion

- -

eval should never be used, any code that makes use of it is to be questioned in -its workings, performance and security. In case something requires eval in -order to work, its design is to be questioned and should not be used in the -first place, a better design should be used, that does not require the use of -eval.

undefined and null

JavaScript has two distinct values for nothing, the more useful of these two -being undefined.

- -

The Value undefined

- -

undefined is a type with exactly one value: undefined.

- -

The language also defines a global variable that has the value of undefined, -this variable is also called undefined. But this variable is not a constant, -nor is it a keyword of the language. This means that its value can be easily -overwritten.

- - - -

Some examples for when the value undefined is returned:

- -
    -
  • Accessing the (unmodified) global variable undefined.
  • -
  • Implicit returns of functions due to missing return statements.
  • -
  • return statements which do not explicitly return anything.
  • -
  • Lookups of non-existent properties.
  • -
  • Function parameters which do not had any explicit value passed.
  • -
  • Anything that has been set to the value of undefined.
  • -
- -

Handling Changes to the Value of undefined

- -

Since the global variable undefined only holds a copy of the actual value of -undefined, assigning a new value to it does not change the value of the -type undefined.

- -

Still, in order to compare something against the value of undefined it is -necessary to retrieve the value of undefined first.

- -

In order to protect code against a possible overwritten undefined variable, a -common technique used is to add an additional parameter to an -anonymous wrapper, that gets no argument passed to it.

- -
var undefined = 123;
-(function(something, foo, undefined) {
-    // undefined in the local scope does 
-    // now again refer to the value
-
-})('Hello World', 42);
-
- -

Another way to achieve the same effect would be to use a declaration inside the -wrapper.

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

The only difference being here, that this version results in 4 more bytes being -used in case it is minified and there is no other var statement inside the -anonymous wrapper.

- -

Uses of null

- -

While undefined in the context of the JavaScript language is mostly used in -the sense of a traditional null, the actual null (both a literal and a type) -is more or less just another data type.

- -

It is used in some JavaScript internals (like declaring the end of the -prototype chain by setting Foo.prototype = null), but in almost all cases it -can be replaced by undefined.

Automatic Semicolon Insertion

Although JavaScript has C style syntax, it does not enforce the use of -semicolons in the source code, it is possible to omit them.

- -

But JavaScript is not a semicolon-less language, it in fact needs the -semicolons in order to understand the source code. Therefore the JavaScript -parser automatically inserts them whenever it encounters a parse -error due to a missing semicolon.

- -
var foo = function() {
-} // parse error, semicolon expected
-test()
-
- -

Insertion happens, and the parser tries again.

- -
var foo = function() {
-}; // no error, parser continues
-test()
-
- -

The automatic insertion of semicolon is considered to be one of biggest -design flaws in the language, as it can change the behavior of code.

- -

How it Works

- -

The code below has no semicolons in it, so it is up to the parser to decide where -to insert them.

- -
(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)
-
- -

Below is the result of the parser's "guessing" game.

- -
(function(window, undefined) {
-    function test(options) {
-
-        // Not inserted, lines got merged
-        log('testing!')(options.list || []).forEach(function(i) {
-
-        }); // <- inserted
-
-        options.value.test(
-            'long string to pass here',
-            'and another long string to pass'
-        ); // <- inserted
-
-        return; // <- inserted, breaks the return statement
-        { // treated as a block
-
-            // a label and a single expression statement
-            foo: function() {} 
-        }; // <- inserted
-    }
-    window.test = test; // <- inserted
-
-// The lines got merged again
-})(window)(function(window) {
-    window.someLibrary = {}; // <- inserted
-
-})(window); //<- inserted
-
- - - -

The parser drastically changed the behavior of the code above, in certain cases -it does the wrong thing.

- -

Leading Parenthesis

- -

In case of a leading parenthesis, the parser will not insert a semicolon.

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

This code gets transformed into one line.

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

Chances are very high that log does not return a function; therefore, -the above will yield a TypeError stating that undefined is not a function.

- -

In Conclusion

- -

It is highly recommended to never omit semicolons, it is also advocated to -keep braces on the same line with their corresponding statements and to never omit -them for one single-line if / else statements. Both of these measures will -not only improve the consistency of the code, they will also prevent the -JavaScript parser from changing its behavior.

その他

setTimeout and 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); // returns a Number > 0
-
- -

When setTimeout gets called, it will return the ID of the timeout and schedule -foo to run in approximately one thousand milliseconds in the future. -foo will then get executed exactly once.

- -

Depending on the timer resolution of the JavaScript engine that is running the -code, as well as the fact that JavaScript is single threaded and other code that -gets executed might block the thread, it is by no means a safe bet that one -will get the exact delay that was specified in the setTimeout call.

- -

The function that was passed as the first parameter will get called by the -global object, that means, that this inside the called function -refers to that very object.

- -
function Foo() {
-    this.value = 42;
-    this.method = function() {
-        // this refers to the global object
-        console.log(this.value); // will log undefined
-    };
-    setTimeout(this.method, 500);
-}
-new Foo();
-
- - - -

Stacking Calls with setInterval

- -

While setTimeout only runs the function once, setInterval - as the name -suggests - will execute the function every X milliseconds. But its use is -discouraged.

- -

When code that is being executed blocks the timeout call, setInterval will -still issue more calls to the specified function. This can, especially with small -intervals, result in function calls stacking up.

- -
function foo(){
-    // something that blocks for 1 second
-}
-setInterval(foo, 100);
-
- -

In the above code foo will get called once and will then block for one second.

- -

While foo blocks the code setInterval will still schedule further calls to -it. Now, when foo has finished, there will already be ten further calls to -it waiting for execution.

- -

Dealing with Possible Blocking Code

- -

The easiest as well as most controllable solution, is to use setTimeout within -the function itself.

- -
function foo(){
-    // something that blocks for 1 second
-    setTimeout(foo, 100);
-}
-foo();
-
- -

Not only does this encapsulate the setTimeout call, but it also prevents the -stacking of calls and it gives additional control.foo itself can now decide -whether it wants to run again or not.

- -

Manually Clearing Timeouts

- -

Clearing timeouts and intervals works by passing the respective ID to -clearTimeout or clearInterval, depending which set function was used in -the first place.

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

Clearing all timeouts

- -

As there is no built-in method for clearing all timeouts and/or intervals, -it is necessary to use brute force in order to achieve this functionality.

- -
// clear "all" timeouts
-for(var i = 1; i < 1000; i++) {
-    clearTimeout(i);
-}
-
- -

There might still be timeouts that are unaffected by this arbitrary number; -therefore, is is instead recommended to keep track of all the timeout IDs, so -they can be cleared specifically.

- -

Hidden use of eval

- -

setTimeout and setInterval can also take a string as their first parameter. -This feature should never be used, since it internally makes use of eval.

- - - -
function foo() {
-    // will get called
-}
-
-function bar() {
-    function foo() {
-        // never gets called
-    }
-    setTimeout('foo()', 1000);
-}
-bar();
-
- -

Since eval is not getting called directly in this case, the string -passed to setTimeout will get executed in the global scope; thus, it will -not use the local variable foo from the scope of bar.

- -

It is further recommended to not use a string for passing arguments to the -function that will get called by either of the timeout functions.

- -
function foo(a, b, c) {}
-
-// NEVER use this
-setTimeout('foo(1,2, 3)', 1000)
-
-// Instead use an anonymous function
-setTimeout(function() {
-    foo(a, b, c);
-}, 1000)
-
- - - -

In Conclusion

- -

Never should a string be used as the parameter of setTimeout or -setInterval. It is a clear sign of really bad code, when arguments need -to be supplied to the function that gets called. An anonymous function should -be passed that then takes care of the actual call.

- -

Further, the use of setInterval should be avoided since its scheduler is not -blocked by executing JavaScript.

\ No newline at end of file diff --git a/site/pl/index.html b/site/pl/index.html deleted file mode 100644 index cfeeb4fa..00000000 --- a/site/pl/index.html +++ /dev/null @@ -1,1914 +0,0 @@ -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 3e3e31da9e7937e85e61d54c16c17001d5297a62 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 00:42:46 +0200 Subject: [PATCH 027/469] Corrections in Polish translation (sections Array and Core). --- doc/pl/array/constructor.md | 14 +++++++------- doc/pl/array/general.md | 28 ++++++++++++++-------------- doc/pl/core/eval.md | 19 +++++++++---------- doc/pl/core/semicolon.md | 22 +++++++++++----------- doc/pl/core/undefined.md | 31 +++++++++++++++---------------- 5 files changed, 56 insertions(+), 58 deletions(-) diff --git a/doc/pl/array/constructor.md b/doc/pl/array/constructor.md index 79772f43..7df61c1d 100644 --- a/doc/pl/array/constructor.md +++ b/doc/pl/array/constructor.md @@ -2,7 +2,7 @@ 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] @@ -14,15 +14,15 @@ parametry do niego przekazane. 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 @@ -31,7 +31,7 @@ pętli `for`. ### 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. diff --git a/doc/pl/array/general.md b/doc/pl/array/general.md index c30056d7..6bd89bae 100644 --- a/doc/pl/array/general.md +++ b/doc/pl/array/general.md @@ -1,15 +1,15 @@ -## Iterowanie po tablicach oraz właściwości tablic +## Iterowanie po tablicach oraz właściwościach tablic -Mimo, że tablice w JavaScript są obiektami, nie ma dobrych powodów do używania -[`pętli for in`](#object.forinloop) do iteracji po nich. W rzeczywiści istnieje -wiele dobrych powodów **przeciwko** wykorzystania `for in` na tablicach. +Mimo że tablice w JavaScript są obiektami, nie ma dobrych powodów aby używać +[`pętli for in`](#object.forinloop) do iteracji po nich. W rzeczywstości istnieje +wiele dobrych powodów **przeciwko** wykorzystaniu `for in` na tablicach. > **Uwaga:** Tablice JavaScriptowe **nie** są *tablicami asocjacyjnymi*. JavaScript > posiada tylko [obiekty](#object.general) do mapowania kluczy do wartości. Jednakże > tablice asocjacyjne **zachowują** porządek, natomiast obiekty **nie zachowują**. 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`](#object.hasownproperty), ale wówczas pętla staje się **dwadzieście razy** wolniejsza od normalnej pętli `for`. @@ -23,22 +23,22 @@ użyć klasycznej pętli `for`. console.log(list[i]); } -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; @@ -54,5 +54,5 @@ Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie 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ść. +oznaką źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność. diff --git a/doc/pl/core/eval.md b/doc/pl/core/eval.md index 6b66b37c..1da5c812 100644 --- a/doc/pl/core/eval.md +++ b/doc/pl/core/eval.md @@ -1,4 +1,4 @@ -## Dlaczego nie należy używać `eval` +## Dlaczego nie należy używać `eval`? Funkcja `eval` uruchomi podany string jako kod JavaScript w lokalnym zasięgu (scopie). @@ -11,8 +11,8 @@ Funkcja `eval` uruchomi podany string jako kod JavaScript w lokalnym zasięgu (s 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`. +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() { @@ -30,20 +30,19 @@ osiągnąć ten sam efekt **nie** używając `eval`. ### `eval` w przebraniu [Funkcje wykonywane po upływie czasu](#other.timeouts) `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`. +należy ponownie przemyśleć i *ulepszyć* aby nie wymagał użycia `eval`. diff --git a/doc/pl/core/semicolon.md b/doc/pl/core/semicolon.md index 6dcaf9cb..7f1410cf 100644 --- a/doc/pl/core/semicolon.md +++ b/doc/pl/core/semicolon.md @@ -1,9 +1,9 @@ ## Automatyczne wstawianie średnika -Mimo, że JavaScript ma składnię podobną do języka C, to **nie** wymusza stosowania +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. @@ -19,7 +19,7 @@ Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt. 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. +konstrukcji języka, ponieważ *może* ono zmienić zachowanie kodu. ### Jak działa wstawianie @@ -84,11 +84,11 @@ Poniżej znajduje się rezultat "zgadywania" parsera. })(window); //<- wstawiony > **Uwaga:** Parser JavaScript nie potrafił "odpowiednio" zinterpretować -> deklaracji return, po którje został dodany znak nowej linii. Mimo, że +> deklaracji return, po której został dodany znak nowej linii. Mimo że > niekoniecznie jest to błąd automatycznego wstawiania średników, to może to > jednak powodować niechciane efekty uboczne -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 @@ -99,19 +99,19 @@ W przypadku, gdy w następnej linii znajduje się nawias, parser **nie** wstawi log('testing!') (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. +`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. diff --git a/doc/pl/core/undefined.md b/doc/pl/core/undefined.md index b938ead3..7e1d056e 100644 --- a/doc/pl/core/undefined.md +++ b/doc/pl/core/undefined.md @@ -8,44 +8,43 @@ z tych dwóch jest `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. +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. > Uwaga ES55: `undefined` w ECMAScript 5 **nie będzie już** *nadpisywalna* w trybie -> strict mode, ale jej nazwa może zostać przysłonona przez na przykład funkcję o +> strict mode, ale jej nazwa może zostać przesłoniona przez na przykład funkcję o > nazwie `undefined`. 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](#function.scopes), 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; @@ -55,7 +54,7 @@ wewnątrz wrappera. })('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` From 51baba21de57c1661d7ab8d8679aeb803327b946 Mon Sep 17 00:00:00 2001 From: HIRAKI Satoru Date: Mon, 4 Jul 2011 08:13:43 +0900 Subject: [PATCH 028/469] fix representation --- doc/ja/intro/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ja/intro/index.md b/doc/ja/intro/index.md index b2fcfb3c..0f7ad2c4 100644 --- a/doc/ja/intro/index.md +++ b/doc/ja/intro/index.md @@ -1,13 +1,13 @@ ## 前書き -**JavaScript Garden** はJavaScriptというプログラム言語の一番奇抜な部分についてのドキュメント集です。 -このドキュメントはJavaScriptという言語に対して不慣れなプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 +**JavaScript Garden** はJavaScriptというプログラム言語の一番奇妙な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に慣れていないプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 JavaScript GardenはJavaScriptを教える事を**目的にしていません**。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkの[ガイド][1] がオススメです。 ## 著者 -このガイドは2人の愛すべき[Stack Overflow][2]ユーザーである[Ivo Wetzel][3] +このガイドは愛すべき[Stack Overflow][2]の2人のユーザー[Ivo Wetzel][3] (執筆)と[Zhang Yi Jiang][4] (デザイン)によって作られました。 ## 貢献者 From 0cc2e8d1e0c88a0389d89b55bb77ae46dc7c2533 Mon Sep 17 00:00:00 2001 From: HIRAKI Satoru Date: Mon, 4 Jul 2011 08:18:04 +0900 Subject: [PATCH 029/469] fix representation of index.json --- doc/ja/index.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ja/index.json b/doc/ja/index.json index cea3fb99..70be20fc 100644 --- a/doc/ja/index.json +++ b/doc/ja/index.json @@ -1,7 +1,7 @@ { "title": "JavaScript Garden", "langTitle": "JavaScript Garden in Japanese", - "description": "JavaScriptの奇抜さと欠陥についてのガイドライン", + "description": "JavaScriptの奇妙さと欠陥についてのガイドライン", "sections": [ { "title": "前書き", From bd4a74bf8132c6cc420f754ab8142737ee058236 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 16:15:32 +0200 Subject: [PATCH 030/469] Corrections in Polish translation (Function and Intro sections). --- doc/pl/core/undefined.md | 4 +-- doc/pl/function/arguments.md | 27 ++++++++-------- doc/pl/function/closures.md | 22 ++++++------- doc/pl/function/constructors.md | 30 +++++++++--------- doc/pl/function/general.md | 14 ++++----- doc/pl/function/scopes.md | 55 ++++++++++++++++----------------- doc/pl/function/this.md | 26 ++++++++-------- doc/pl/intro/license.md | 2 +- doc/pl/intro/translators.md | 4 ++- 9 files changed, 92 insertions(+), 92 deletions(-) diff --git a/doc/pl/core/undefined.md b/doc/pl/core/undefined.md index 7e1d056e..6fb9ac95 100644 --- a/doc/pl/core/undefined.md +++ b/doc/pl/core/undefined.md @@ -7,7 +7,7 @@ z tych dwóch jest `undefined`. `undefined` jest typem z dokładnie jedną wartością: `undefined`. -Język również definiuje globalną zmienną, która ma wartość `undefined`, zmienna +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. @@ -60,7 +60,7 @@ 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 diff --git a/doc/pl/function/arguments.md b/doc/pl/function/arguments.md index 9f5d7476..098c2de9 100644 --- a/doc/pl/function/arguments.md +++ b/doc/pl/function/arguments.md @@ -1,18 +1,18 @@ ## Obiekt `arguments` -Każda zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej `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. -> **Uwaga:** W przypadku gdy `arguments` zostanie zadeklarowana wewnatrz funkcji +> **Uwaga:** W przypadku gdy `arguments` zostanie zadeklarowana wewnątrz funkcji > poprzez `var` lub jako nazwa jednego z formalnych parametrów, obiekt `arguments` > nie zostanie utworzony. -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`. @@ -29,7 +29,7 @@ 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. +wyglada następująco: function foo() { bar.apply(null, arguments); @@ -58,11 +58,11 @@ szybkich i nieograniczonych wrapperów. ### 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; @@ -79,7 +79,7 @@ odpowiadającemu mu wpisowi w obiekcie `arguments`, zachodzi to również w drug ### 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. @@ -108,9 +108,8 @@ nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie W powyższym przykładzie `foo` nie może zostać wykorzystana metoda [inline][1] 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. diff --git a/doc/pl/function/closures.md b/doc/pl/function/closures.md index c8e45b99..8cb6fb79 100644 --- a/doc/pl/function/closures.md +++ b/doc/pl/function/closures.md @@ -1,7 +1,7 @@ ## 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 +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ę](#function.scopes), wszystkie funkcje domyślnie zachowują się jak domknięcia. @@ -24,16 +24,16 @@ został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko po 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 +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() { @@ -48,7 +48,7 @@ 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 +aby wartość zmiennej po której odbywa się iteracja była kopiowana do wewnętrznej funkcji. for(var i = 0; i < 10; i++) { @@ -80,8 +80,8 @@ z [anonimowego wrappera](#function.scopes). })(i); } -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 diff --git a/doc/pl/function/constructors.md b/doc/pl/function/constructors.md index 4cb7bd12..4e0f502e 100644 --- a/doc/pl/function/constructors.md +++ b/doc/pl/function/constructors.md @@ -1,7 +1,7 @@ ## 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 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 @@ -43,7 +43,7 @@ obiekt `Object`. } 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() { @@ -51,13 +51,13 @@ obiektu. } Foo(); // undefined -Mimo, że powyższy kod może zadziałać w pewnych przypadkach, w związku -z działaniem [`this`](#function.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`](#function.this) w języku JavaScript, to jako wartość `this`zostanie 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; @@ -74,13 +74,13 @@ Aby móc ominąć słowo kluczowe `new` konstruktor musi jawnie zwracać wartoś 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](#function.closures). +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](#function.closures). 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 @@ -88,8 +88,8 @@ 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. +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. @@ -110,14 +110,14 @@ nowy obiekt wewnątrz tej fabryki. } -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](#function.closures), to posiada pewne wady. While the above is robust against a missing `new` keyword and certainly makes the use of [private variables](#function.closures) 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. @@ -129,5 +129,5 @@ downsides. 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ć**. +ważne jest, aby wybrać określony styl tworzenia obiektów i **trzymać** się go. diff --git a/doc/pl/function/general.md b/doc/pl/function/general.md index d55153c3..e3f035b2 100644 --- a/doc/pl/function/general.md +++ b/doc/pl/function/general.md @@ -1,6 +1,6 @@ ## Deklaracje funkcji i wyrażenia funkcyjne -Funcje w języku JavaScript są [typami pierwszoklasowymi][1]. Co oznacza, że mogą +Funcje w języku JavaScript są [typami pierwszoklasowymi][1], 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. @@ -9,9 +9,9 @@ asynchronicznej funkcji. function foo() {} -Powyższa funkcja zostaje [wyniesiona](#function.scopes) zanim program wystartuje, dzięki temu +Powyższa funkcja zostaje [wyniesiona](#function.scopes) 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 @@ -27,7 +27,7 @@ Ten przykład przypisuje nienazwaną i *anonimową* funkcję do zmiennej `foo`. foo(); // wyrzuca błąd TypeError 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. @@ -35,7 +35,7 @@ Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość `foo` ustawiona na domyślną wartość [undefined](#core.undefined) zanim powyższy kod zostanie uruchomiony. -### Nazwane wyrażenia funkdyjne +### Nazwane wyrażenia funkcyjne Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji. @@ -44,10 +44,10 @@ Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji. } 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](#function.scopes) w języku JavaScript. Nazwa funkcji jest *zawsze* dostępna w lokalnym zakresie tej funkcji. -[1]: http://pl.wikipedia.org/wiki/Typ_pierwszoklasowy \ No newline at end of file +[1]: http://pl.wikipedia.org/wiki/Typ_pierwszoklasowy diff --git a/doc/pl/function/scopes.md b/doc/pl/function/scopes.md index a06eba05..52126570 100644 --- a/doc/pl/function/scopes.md +++ b/doc/pl/function/scopes.md @@ -1,6 +1,6 @@ ## Zasięg zmiennych i przestrzenie nazw -Mimo, że JavaScript radzi sobie dobrze ze składnią, opisującą dwa pasujące +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*. @@ -12,17 +12,16 @@ Jedynym zasięgiem jaki istnieje w JavaScript jest *zasięg funkcyjny*. } > **Uwaga:** Jeżeli notacja `{...}` nie jest użyta w przypisaniu, deklaracji return -> lub jako argument funkcji to zostanie zinterpretowana jako deklaracja bloku, a -> **nie** jako literał obiektu. W połączeniu z [automatycznym wstawianiem średnika](#core.semicolon), +> lub jako argument funkcji, to zostanie zinterpretowana jako deklaracja bloku, +> a **nie** jako literał obiektu. W połączeniu z [automatycznym wstawianiem średnika](#core.semicolon), > może prowadzić do subtelnych błędów. 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 @@ -32,11 +31,11 @@ zasięgu i nadal nie znajdzie żądanej nazwy to wyrzuca błąd `ReferenceError` // 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` +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 @@ -49,8 +48,8 @@ poważne konsekwencje. 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` +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 @@ -68,7 +67,7 @@ wprowadzi straszne i trudne do wyśledzenia błędy. 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. @@ -144,15 +143,15 @@ najbliższego zasięgu. 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) { @@ -166,7 +165,7 @@ przeniesiona na początek *globalnego zasięgu*. // 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 = {}; } @@ -174,7 +173,7 @@ przeniesiona na początek *globalnego zasięgu*. ### Kolejność rozwiązywania nazw Wszystkie zasięgi w JavaScripcie, włączając *globalny zasięg*, posiadają -zdefiniowana wewnątrz specjalną nazwę [`this`](#function.this), która wskazuje +zdefiniowaną wewnątrz specjalną nazwę [`this`](#function.this), która wskazuje na *aktualny obiekt*. Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę @@ -188,8 +187,8 @@ JavaScript będzie szukać nazwy w określonej kolejności: 3. Jeżeli fukcja została nazwana `foo` skorzystaj z tego. 4. Przejdz do zewnętrznego zasięgu i przejdz do kroku **#1**. -> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments` zapobiegnie -> to utworzenia domyślnego obiektu `arguments`. +> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments`, zapobiegnie +> to utworzeniu domyślnego obiektu `arguments`. ### Przestrzenie nazw @@ -206,16 +205,16 @@ tego problemu korzystając z *anonimowych wrapperów*. })(); // natychmiastowe wykonanie funkcji -Nienazwane funkcje są rozpoznane jako [wyrażenia](#function.general), więc +Anonimowe funkcje są rozpoznane jako [wyrażenia](#function.general), 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(){}(); @@ -224,11 +223,11 @@ mają inną składnie, zachowują się dokładnie tak samo. ### 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. +Wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, który +jest podatny na błędy i trudny do utrzymania. diff --git a/doc/pl/function/this.md b/doc/pl/function/this.md index 8fc8564d..89a5a1e5 100644 --- a/doc/pl/function/this.md +++ b/doc/pl/function/this.md @@ -1,8 +1,8 @@ ## 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 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 @@ -12,7 +12,7 @@ ways in which the value of `this` can be bound in the language. 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 @@ -34,7 +34,7 @@ W tym przypadku `this` będzie wskazywało na `test`. 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](#function.constructors). Wewnątrz funkcji `this` będzie wskazywało na *nowo utworzony* obiekt. @@ -70,10 +70,10 @@ w praktyce. test(); } -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() { @@ -84,13 +84,13 @@ metody lokalnej zmiennej, która będzie wskazywała na `Foo`. test(); } -`that` jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja, aby otrzymać -wartość zewnętrznego `this`. W połączeniu z [domknięciami(closures)](#function.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)](#function.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; @@ -100,7 +100,7 @@ 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 +to w rzeczywistości jest to rzecz, która sprawia, że [dziedziczenie prototypowe](#object.prototype) działa. function Foo() {} @@ -112,5 +112,5 @@ to w rzeczywistości jest to rzecz, która powoduje że new Bar().method(); Kiedy metoda `method` zostanie wywołana na instancji `Bar`, `this` będzie -wskazywało właśnie tą instancję. +wskazywało właśnie tę instancję. diff --git a/doc/pl/intro/license.md b/doc/pl/intro/license.md index 7e2c77d0..c9329aab 100644 --- a/doc/pl/intro/license.md +++ b/doc/pl/intro/license.md @@ -2,7 +2,7 @@ JavaScript Garden jest publikowany w ramach [licencji MIT] [1] i kod źródłowy znajduje się na serwerze [GitHub] [2]. Jeśli znajdziesz jakieś błędy lub literówek zgłoś proszę -[problem] [3] lub rozwiązag go i zglosić pull request ze swojego repozytorium. +[problem] [3] lub rozwiąż go i zgloś pull request ze swojego repozytorium. Możesz nas także znaleźć w pokoju [JavaScript] [4] na chacie Stack Overflow. [1]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE diff --git a/doc/pl/intro/translators.md b/doc/pl/intro/translators.md index 60b3626e..5197a9fd 100644 --- a/doc/pl/intro/translators.md +++ b/doc/pl/intro/translators.md @@ -1,5 +1,7 @@ ## Tłumaczenie - [Łukasz Kufel][1] + - [Maciej Ciemborowicz][2] -[1]: http://qfel13.pl \ No newline at end of file +[1]: http://qfel13.pl +[2]: http://blog.ciemborowicz.pl From e2c62e07e100e6fa8e5e4a5f55778ea5edd584f7 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:02:42 +0200 Subject: [PATCH 031/469] Corrections in Polish translation (Object section). --- doc/pl/object/forinloop.md | 30 +- doc/pl/object/general.md | 36 +- doc/pl/object/hasownproperty.md | 22 +- doc/pl/object/prototype.md | 58 +- site/pl/index.html | 1911 +++++++++++++++++++++++++++++++ 5 files changed, 1984 insertions(+), 73 deletions(-) create mode 100644 site/pl/index.html diff --git a/doc/pl/object/forinloop.md b/doc/pl/object/forinloop.md index a2e2b5fe..9a6654a6 100644 --- a/doc/pl/object/forinloop.md +++ b/doc/pl/object/forinloop.md @@ -1,11 +1,11 @@ -## The `for in` Loop +## 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. > **Uwaga:** pętla `for in` **nie** będzie iterować po właściwościach, które -> mają ustawiony atrybut `enumerable` na `false` na przykład właściwość -> `length` tablicy. +> mają ustawiony atrybut `enumerable` na `false` (na przykład właściwość +> `length` tablicy). // Zatrucie Object.prototype Object.prototype.bar = 1; @@ -15,15 +15,15 @@ podczas iteracji po właściwościach obiektu. 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 +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`](#object.hasownproperty) z `Object.prototype`. -> **Uwaga:** Ponieważ pętla `for in` zawsze przeszukuje cały łańcuch prototypów +> **Uwaga:** Ponieważ pętla `for in` zawsze przeszukuje cały łańcuch prototypów, > będzie się ona stawała coraz wolniejsza przy dodaniu każdej kolejnej warstwy > dziedziczenia do obiektu. -### Korzystanie z `hasOwnProperty` do odfiltrowania +### Filtrowania przy użyciu `hasOwnProperty` // foo z przykładu powyżej for(var i in foo) { @@ -32,19 +32,19 @@ z metody [`hasOwnProperty`](#object.hasownproperty) z `Object.prototype`. } } -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][1] 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][1] 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. +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. [1]: http://www.prototypejs.org/ diff --git a/doc/pl/object/general.md b/doc/pl/object/general.md index 24a074e3..e4ff6a96 100644 --- a/doc/pl/object/general.md +++ b/doc/pl/object/general.md @@ -10,9 +10,9 @@ Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami 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 +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 @@ -22,27 +22,27 @@ jak obiekt. 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*][1]. -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*][1], +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ł](#object.prototype) z `Object.prototype` oraz -nie bedzie posiadał żadnych [własnych właściwości](#object.hasownproperty) zdefiniowanych w sobie. +nie bedzie posiadał żadnych [własnych właściwości](#object.hasownproperty). - 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}; ### 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 @@ -54,8 +54,8 @@ lub notacje z nawiasami kwadratowymi. 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 +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 @@ -79,19 +79,19 @@ związaną z własnością, ale nie usunie to *klucza* (nazwy własności) z obi } } -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 zastrzeżonym 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ć diff --git a/doc/pl/object/hasownproperty.md b/doc/pl/object/hasownproperty.md index 5c3429e6..d9366447 100644 --- a/doc/pl/object/hasownproperty.md +++ b/doc/pl/object/hasownproperty.md @@ -1,13 +1,13 @@ ## `hasOwnProperty` -W celu sprawdzenia czy dana właściwość została zdefiniowana *w tym* obiekcie a **nie** -w [łańcuchu prototypów](#object.prototype) niezbędne jest skorzystanie z metody -`hasOwnProperty`, która wszystkie obiekty dziedziczą z `Object.prototype`. +W celu sprawdzenia, czy dana właściwość została zdefiniowana *w tym* obiekcie, a **nie** +w [łańcuchu prototypów](#object.prototype), niezbędne jest skorzystanie z metody +`hasOwnProperty`, której wszystkie obiekty dziedziczą z `Object.prototype`. -> **Uwaga:** **Nie** jest wystarczające, by sprawdzić, czy właściwość jest `undefined`. -> Ponieważ właściwość może istnieć, ale jej wartość być ustawiona na `undefined`. +> **Uwaga:** **Nie** wystarczy sprawdzić, czy właściwość jest `undefined`, +> ponieważ właściwość może istnieć, ale jej wartość być ustawiona na `undefined`. -`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 @@ -28,7 +28,7 @@ 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 +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 = { @@ -46,8 +46,8 @@ możliwość, że obiekt może posiadać tak nazwaną właściwość. Konieczne ### 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`](#object.forinloop), pozwoli to uniknąć błędów pochodzących z -rozszerzonych natywnych [prototypów](#object.prototype). +**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`](#object.forinloop). Pozwoli to uniknąć błędów pochodzących +z rozszerzonych natywnych [prototypów](#object.prototype). diff --git a/doc/pl/object/prototype.md b/doc/pl/object/prototype.md index e79a45cb..85dbdfb7 100644 --- a/doc/pl/object/prototype.md +++ b/doc/pl/object/prototype.md @@ -1,23 +1,23 @@ ## Prototyp -JavaScript nie posiada klasycznego modelu dziedziczenia, lecz zamiast tego +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*. -> **Uwaga:** Używanie po prosstu `Bar.prototype = Foo.prototype` spowoduje, że oba obiekty -> będą korzystały z **tego samego** prototypu. W związku z tym zmiany na prototypie jednego -> obiektu będą również zmieniały prototyp drugiego obiektu, co jest w wiekszości przypadków +> **Uwaga:** Używanie po prostu `Bar.prototype = Foo.prototype` spowoduje, że oba obiekty +> będą korzystały z **tego samego** prototypu. W związku z tym zmiany w prototypie jednego +> obiektu będą również zmieniały prototyp drugiego obiektu, co jest ,w wiekszości przypadków, > niepożądanym efektem. function Foo() { @@ -47,25 +47,25 @@ tak zwanych *łańcuchów prototypów*. Object.prototype { 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`. > **Uwaga:** **Nie** należy używać konstrukcji `Bar.prototype = Foo`, -> ponieważ nie spowoduje ona przypisania prototypu `Foo` a raczej obiektu +> ponieważ nie spowoduje ona przypisania prototypu `Foo` tylko obiektu > funckji `Foo`. Zatem łańcuch prototypów nie bedzie zawierał `Foo.prototype`, -> ale `Function.prototype`, więc metoda `method` nie będzie w łańcuchu prototypów. +> tylko `Function.prototype`, więc metoda `method` nie będzie w łańcuchu prototypów. ### 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](#core.undefined). @@ -85,31 +85,31 @@ na dynamiczne tworzenie łańcuchów prototypów. 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](#object.forinloop) 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](#object.forinloop) 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][1] i łamie zasady *enkapsulacji*. -Jednak jest szeroko rozpowszechniona w frameworkach takich jak [Prototype][2]. -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][2]. +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`][3] +funkcjonalności znajdujących sie w nowszych silnikach JavaScript, np. [`Array.forEach`][3] ### 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. diff --git a/site/pl/index.html b/site/pl/index.html new file mode 100644 index 00000000..872f9cd0 --- /dev/null +++ b/site/pl/index.html @@ -0,0 +1,1911 @@ +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ąż 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.

+ +
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 funkcyjne

+ +

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ę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 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 +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 referencję 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 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 +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 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 +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 sprawia, ż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 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 prywatne działają?

+ +

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() {
+    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ła 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 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 +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ż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 w rzeczywistości nie dziedziczy +on z Array.prototype, tylko z Object.

+ +

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.

+ +

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 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ę.

+ +
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 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.

+ +

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 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.

+ +

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 +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 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). +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 +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ż 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.

+ +
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 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.

+ +
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.

+ +

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 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ć +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 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 
+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ł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.

+ +

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ętli, +ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.

+ +

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 sprawiać wrażenie, +że zobaczymy 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ą +zdefiniowaną 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
+
+ +

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

+ +
( // 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ładnię, 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ę. +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 jest 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]);
+}
+
+ +

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 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ć 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 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 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;
+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 +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 +przekazane do niego parametry.

+ +
[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ść 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
+// stringToRepeat - ciąg znaków do powtórzenia 
+new Array(count + 1).join(stringToRepeat); 
+
+ +

Wnioski

+ +

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

+ +

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 wtedy, gdy zostanie wywołana +bezpośrednio i nazwa wywoływanej 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 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 nigdy nie należy +jej używać na stringach nieznanego lub niezaufanego pochodzenia.

+ +

Wnioski

+ +

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 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. 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 czemu została przypisana wartość undefined.
  • +
+ +

Obsługa przypadku zmiany wartości undefined

+ +

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ś z wartością undefined, trzeba odczytać wartość undefined.

+ +

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 o lokalnym zasięgu znowu 
+    // odnosi się do poprawnej wartości
+
+})('Hello World', 42);
+
+ +

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

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

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, 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.

+ +

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 zmienić 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) {})
+
+ +

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 +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 też zapobiegają zmianie działania +kodu przez parser JavaScript.

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 bbe88bba290d486d50b6a725541b43fbc9ce4c49 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:17:52 +0200 Subject: [PATCH 032/469] Corrections in Polish translation (Other section). --- doc/pl/other/timeouts.md | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/pl/other/timeouts.md b/doc/pl/other/timeouts.md index bf2ad512..9cf78d13 100644 --- a/doc/pl/other/timeouts.md +++ b/doc/pl/other/timeouts.md @@ -1,7 +1,7 @@ ### `setTimeout` i `setInterval` Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania -funkcji korzystając z funkcji `setTimeout` i `setInterval`. +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. @@ -9,7 +9,7 @@ function by using the `setTimeout` and `setInterval` functions. > standardu [DOM][1]. 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 **około** tysiąc milisekund. `foo` zostanie wykonana dokładnie **jeden raz**. @@ -18,11 +18,11 @@ Powyższe wywołanie `setTimeout` zwraca ID budzika i planuje wywołanie `foo` z 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`](#function.this) wewnątrz tej funkcji -będzie wkazywać na obiekt *global*. +będzie wskazywać na obiekt *global*. function Foo() { this.value = 42; @@ -35,22 +35,22 @@ będzie wkazywać na obiekt *global*. new Foo(); > **Uwaga:** Ponieważ `setTimeout` przyjmuje **obiekt funkcji** jako pierwszy -> argument często popełnianym błędem jest wykorzystanie składni `setTimeout(foo(), 1000)`, +> argument, często popełnianym błędem jest wykorzystanie składni `setTimeout(foo(), 1000)`, > która użyje wartości zwróconej przez funkcję `foo` jako parametru zamiast > funkcji `foo` samej w sobie. W większości przypadków będzie to cichy błąd, -> ponieważ jeżeli funkcja zwróci `undefined` `setTimeout` **nie** wyrzuci żadnego +> ponieważ jeżeli funkcja zwróci `undefined`, `setTimeout` **nie** wyrzuci żadnego > błędu. ### 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ę @@ -60,13 +60,13 @@ to wydarzyć przy krótkim interwale. 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(){ @@ -75,9 +75,9 @@ wewnątrz wywoływanej funkcji. } 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 @@ -90,17 +90,17 @@ zwrócone 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. +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++) { 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ąć. +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` @@ -125,11 +125,11 @@ możliwości, ponieważ wewnętrznie `setTimeout` i `setInterval` wykorzystują } bar(); -Ponieważ `eval` nie zostało wywołane [wprost](#core.eval) 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](#core.eval), 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) {} @@ -137,23 +137,23 @@ do funkcji, która ma zostać wywołana przez budzik. // 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) ->**Uwaga:** Mimo, że możliwe jest wykorzystanie składni -> `setTimeout(foo, 1000, a, b, c)`, to nie zaleca się korzystania z niej, ponieważ +>**Uwaga:** Mimo że możliwe jest wykorzystanie składni +> `setTimeout(foo, 1000, a, b, c)`, nie zaleca się korzystania z niej, ponieważ > może to prowadzić do subtelnych błędów podczas wykorzystania [metod](#function.this). ### 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. [1]: http://pl.wikipedia.org/wiki/Obiektowy_model_dokumentu "Document Object Model" From 120540c3743c58a46bc0cceeaf9314c82239b638 Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:35:54 +0200 Subject: [PATCH 033/469] Corrections in Polish translation (Types section). --- doc/pl/types/casting.md | 26 +++++++++++++------------- doc/pl/types/equality.md | 18 +++++++++--------- doc/pl/types/instanceof.md | 8 ++++---- doc/pl/types/typeof.md | 8 ++++---- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/pl/types/casting.md b/doc/pl/types/casting.md index 5e358c63..7c676ee1 100644 --- a/doc/pl/types/casting.md +++ b/doc/pl/types/casting.md @@ -1,43 +1,43 @@ ## Rzutowanie typów -JavaScript jest językiem słabo typowanym, co za tym idzie będzie stosować koercję +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'; -> **Uwaga ES5: Literały licznowe zaczynające sie od `0` są interpretowane jako +> **Uwaga ES5: Literały liczbowe zaczynające sie od `0` są interpretowane jako > liczby w systemie ósemkowym. W trybie strict mode w ECMAScript 5 wsparcie dla > liczb ósemkowych zostało porzucone. -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](#types.equality). 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. @@ -58,7 +58,7 @@ Zastosowanie **unarnego** operatora + spowoduje rzutowanie do typu Number. ### 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 diff --git a/doc/pl/types/equality.md b/doc/pl/types/equality.md index e79b952f..48f4197a 100644 --- a/doc/pl/types/equality.md +++ b/doc/pl/types/equality.md @@ -21,16 +21,16 @@ JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości 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 @@ -49,8 +49,8 @@ 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`. +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 @@ -59,13 +59,13 @@ to zachowują się różnie gdy jednym z operandów jest obiekt typu `Object`. 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. +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](#types.casting) a nie pozostawiona trudnym regułom koercji +być dokonana [jawnie](#types.casting), a nie pozostawiona trudnym regułom koercji obowiązującym w języku. diff --git a/doc/pl/types/instanceof.md b/doc/pl/types/instanceof.md index 73ae4ebd..aee5fe86 100644 --- a/doc/pl/types/instanceof.md +++ b/doc/pl/types/instanceof.md @@ -1,8 +1,8 @@ ## 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 +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](#types.typeof). ### Porównywanie obiektów utworzonych klas @@ -14,7 +14,7 @@ go na wbudowanych typach jest praktycznie tak samo bezużyteczne jak operatora 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 @@ -34,7 +34,7 @@ samymi obiektami. ### 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`](#types.typeof), należy **unikać** korzystania z tego operatora w innych sytuacjach. diff --git a/doc/pl/types/typeof.md b/doc/pl/types/typeof.md index 186e241f..ccbc21d1 100644 --- a/doc/pl/types/typeof.md +++ b/doc/pl/types/typeof.md @@ -1,10 +1,10 @@ ## Operator `typeof` Operator `typeof` (razem z operatorem [`instanceof`](#types.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. @@ -83,5 +83,5 @@ są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnyc 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**. +zdefiniowana, powinien być unikany **jeśli to tylko możliwe**. From 64a603ee551b7f2b565e52ec249046a273441ecc Mon Sep 17 00:00:00 2001 From: ciembor Date: Mon, 4 Jul 2011 18:49:31 +0200 Subject: [PATCH 034/469] Small correction in Polish translation. --- doc/pl/object/general.md | 2 +- server.js | 2 +- site/pl/index.html | 272 +++++++++++++++++++-------------------- 3 files changed, 138 insertions(+), 138 deletions(-) diff --git a/doc/pl/object/general.md b/doc/pl/object/general.md index e4ff6a96..9512c293 100644 --- a/doc/pl/object/general.md +++ b/doc/pl/object/general.md @@ -85,7 +85,7 @@ została usunięta i dlatego nie została wypisana. ### Notacja właściwości var test = { - 'case': 'jestem zastrzeżonym słowem kluczowym, więc muszę być w cudzysłowie', + 'case': 'jestem słowem kluczowym, więc muszę być w cudzysłowie', delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError }; diff --git a/server.js b/server.js index fb64b73d..1ead3c14 100644 --- a/server.js +++ b/server.js @@ -2,7 +2,7 @@ var build = require('./build').build, qs = require('querystring'), port = 9900, - repoURL = "https://github.com/cramerdev/JavaScript-Garden"; + repoURL = "https://github.com/ciembor/JavaScript-Garden"; require('http').createServer(function (request, response) { var payload = ''; diff --git a/site/pl/index.html b/site/pl/index.html index 872f9cd0..7542ba1d 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 @@ -32,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
@@ -45,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}; 
@@ -66,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
@@ -80,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

@@ -106,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 zastrzeżonym 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.

@@ -174,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.

@@ -215,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
@@ -272,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 = {
@@ -291,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
@@ -312,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) {
@@ -332,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.

@@ -1242,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
@@ -1271,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
@@ -1282,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.

@@ -1382,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

@@ -1396,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 @@ -1418,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.

@@ -1485,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
@@ -1734,7 +1734,7 @@
 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 korzystając z funkcji setTimeout i setInterval. +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.

@@ -1744,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 @@ -1754,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;
@@ -1773,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ę 
@@ -1800,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(){
@@ -1816,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

@@ -1832,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++) {
@@ -1841,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

@@ -1871,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) {}
@@ -1883,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.