From 7db7bcda4bd48cc3c8b7a015b7ac12066f26d387 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 25 Apr 2015 19:19:29 +0300 Subject: [PATCH 001/227] Ops. Small typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09b8ea5..ee03665 100755 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Follow some of the masters on Twitter. *(See `section 3.6` of the RESOURCES section)* * Code using a [Style Guide](https://github.com/airbnb/javascript). -### 2. SUGGESTED CODING RULES AND BEST PRACTIVES +### 2. SUGGESTED CODING RULES AND BEST PRACTICES * Always use `var` when declaring a variable * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) From bd2c455642b1220c472a8c519a14761da951923f Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 1 Jul 2015 02:08:33 +0300 Subject: [PATCH 002/227] Replacing deprecated fs.existsSync Updating since fs.exists & fs.existsSync are being deprecated. --- snippets/Node-Create-Folder.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/snippets/Node-Create-Folder.js b/snippets/Node-Create-Folder.js index bb42a35..463fccb 100644 --- a/snippets/Node-Create-Folder.js +++ b/snippets/Node-Create-Folder.js @@ -1,8 +1,26 @@ var fs = require('fs'); var dir = './tmp'; -if ( !fs.existsSync( dir ) ){ +function dirExistsSync(d) { + try { + fs.statSync(d).isDirectory(); + return true; + } catch (e) { + return false; + } +} + +if ( ! dirExistsSync(dir) ){ + console.log( "Creating directory " + dir ); + fs.mkdirSync( dir ); +} + +/* Deprecated: fs.exists, fs.existsSync + +if ( ! fs.existsSync( dir ) ){ fs.mkdirSync( dir ); -} \ No newline at end of file +} + +*/ From 7c004bbf207d052e997da830941bf904b07138dd Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 1 Jul 2015 10:23:57 +0300 Subject: [PATCH 003/227] Create Node-Parse-File-By-Line.js --- snippets/Node-Parse-File-By-Line.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 snippets/Node-Parse-File-By-Line.js diff --git a/snippets/Node-Parse-File-By-Line.js b/snippets/Node-Parse-File-By-Line.js new file mode 100644 index 0000000..07e6e58 --- /dev/null +++ b/snippets/Node-Parse-File-By-Line.js @@ -0,0 +1,19 @@ +var fs = require('fs'); +var readline = require('readline'); +var filename = '/path/to/file'; + +var rd = readline.createInterface({ + + input : fs.createReadStream( filename ), + output : process.stdout, + terminal : false + +}); + +rd.on( 'line', function(line) { + + console.log( line ); + +}); + +/* http://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js */ From b174abc82c936531a5d5938911b1be7dfc24f681 Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 1 Jul 2015 10:34:05 +0300 Subject: [PATCH 004/227] Adding close event --- snippets/Node-Parse-File-By-Line.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/snippets/Node-Parse-File-By-Line.js b/snippets/Node-Parse-File-By-Line.js index 07e6e58..7e879ca 100644 --- a/snippets/Node-Parse-File-By-Line.js +++ b/snippets/Node-Parse-File-By-Line.js @@ -2,7 +2,7 @@ var fs = require('fs'); var readline = require('readline'); var filename = '/path/to/file'; -var rd = readline.createInterface({ +var read = readline.createInterface({ input : fs.createReadStream( filename ), output : process.stdout, @@ -10,10 +10,16 @@ var rd = readline.createInterface({ }); -rd.on( 'line', function(line) { +read.on( 'line', function(line) { console.log( line ); }); +read.on( 'close', function() { + + console.log( "Finished parsing " + filename ); + +}); + /* http://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js */ From ce90198f4828cff6afd08568c4732363b85475e3 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 3 Jul 2015 09:13:37 +0300 Subject: [PATCH 005/227] Create RegExp-Parse-CSS.js --- snippets/RegExp-Parse-CSS.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 snippets/RegExp-Parse-CSS.js diff --git a/snippets/RegExp-Parse-CSS.js b/snippets/RegExp-Parse-CSS.js new file mode 100644 index 0000000..4b5fa5c --- /dev/null +++ b/snippets/RegExp-Parse-CSS.js @@ -0,0 +1,13 @@ +var regexpCSS = /([\s\S]*?){([\s\S]*?)}/; + +var css = " .someclass { margin: 10px; padding: 20px; }"; +var match = regexpCSS.exec(css); + +console.log( + + "\n Selector:", match[1], + "\n Declaration Block:", match[2] + +); + +/* Reference: http://www.devbattles.com/en/sand/post-1423-Writing+a+CSS+Parser+in+JavaScript */ From ea54b375c794e47ab256fa9e41670013a173879d Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 3 Jul 2015 09:16:35 +0300 Subject: [PATCH 006/227] Update RegExp-Parse-CSS.js --- snippets/RegExp-Parse-CSS.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snippets/RegExp-Parse-CSS.js b/snippets/RegExp-Parse-CSS.js index 4b5fa5c..d554267 100644 --- a/snippets/RegExp-Parse-CSS.js +++ b/snippets/RegExp-Parse-CSS.js @@ -6,7 +6,7 @@ var match = regexpCSS.exec(css); console.log( "\n Selector:", match[1], - "\n Declaration Block:", match[2] + "\n Declaration Block / Rules:", match[2] ); From d6500e9593bbf7d17dc8ae0c276e80ab998f4f40 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 3 Jul 2015 09:24:19 +0300 Subject: [PATCH 007/227] Adding media queries regular expression snippet. --- snippets/RegExp-Parse-CSS.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/snippets/RegExp-Parse-CSS.js b/snippets/RegExp-Parse-CSS.js index d554267..4062b33 100644 --- a/snippets/RegExp-Parse-CSS.js +++ b/snippets/RegExp-Parse-CSS.js @@ -10,4 +10,7 @@ console.log( ); +// MEDIA QUERIES +// var regexpCSS_Media_Query = /((\s*?@media[\s\S]*?){([\s\S]*?)}\s*?})|(([\s\S]*?){([\s\S]*?)})/; + /* Reference: http://www.devbattles.com/en/sand/post-1423-Writing+a+CSS+Parser+in+JavaScript */ From 581c7ea99b695208494dc9690f43059a26145409 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 3 Jul 2015 09:25:15 +0300 Subject: [PATCH 008/227] Adding online test. --- snippets/RegExp-Parse-CSS.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snippets/RegExp-Parse-CSS.js b/snippets/RegExp-Parse-CSS.js index 4062b33..30bfe92 100644 --- a/snippets/RegExp-Parse-CSS.js +++ b/snippets/RegExp-Parse-CSS.js @@ -10,6 +10,8 @@ console.log( ); +// See it in action: https://regex101.com/r/sQ2fZ2/1 + // MEDIA QUERIES // var regexpCSS_Media_Query = /((\s*?@media[\s\S]*?){([\s\S]*?)}\s*?})|(([\s\S]*?){([\s\S]*?)})/; From cec82cf9b0b79908049c245ba138f3ac0a192787 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 3 Jul 2015 09:28:44 +0300 Subject: [PATCH 009/227] Adding "Things Every JS Developer Should Know" --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ee03665..b364aaf 100755 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [JS 101](http://www.teaching-materials.org/javascript/) * [7 JavaScript Basics Many Developers Aren't Using (Properly)](http://tech.pro/tutorial/1453/7-javascript-basics-many-developers-aren-t-using-properly) * [5 Tips to Become a Better JavaScript Developer](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) +* [Things Every Javascript Developer Should Know, by Lubomir Vitol](http://www.devbattles.com/en/sand/post-1427-Things+Every+Javascript+Developer+Should+Know) * [JavaScript Idiosyncrasies](https://github.com/miguelmota/javascript-idiosyncrasies) * [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) * [Context or the "This" Keyword in JavaScript, *by Adam Breindel*](https://thenewcircle.com/s/post/1564/context_or_the_this_keyword_in_javascript_tutorial) From c070b9bfe95685320edb905b196c414e623edff5 Mon Sep 17 00:00:00 2001 From: Kostas Date: Mon, 6 Jul 2015 09:33:57 +0300 Subject: [PATCH 010/227] Adding "JavaScript the Right Way" --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b364aaf..ded7217 100755 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s #### 3.1 BEGINNER RESOURCES +* [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) * [JavaScript Guide, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) From 5538587ef20c9802029e7fe20a72271f643d7106 Mon Sep 17 00:00:00 2001 From: Kostas Date: Tue, 7 Jul 2015 10:04:40 +0300 Subject: [PATCH 011/227] Adding Awesome Backbone resources list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ded7217..4c3c914 100755 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ##### 3.4.1 BACKBONE.JS +* [**Awesome Backbone** List of Resources](https://github.com/instanceofpro/awesome-backbone/blob/master/README.md) * [Introduction to Backbone Js and Setting Up an Working Environment – Learning Backbone Js](http://www.codebeerstartups.com/2012/12/introduction-to-backbone-js-and-setting-up-an-working-environment/) * [Single Page ToDo Application With Backbone.js](http://code.tutsplus.com/tutorials/single-page-todo-application-with-backbonejs--cms-21417) * [Developing Backbone.js Applications, *by Addy Osmani*](https://github.com/addyosmani/backbone-fundamentals) From 6b5106818a741001bcc2dcf339a047ae310d5344 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 7 Jul 2015 10:56:53 +0300 Subject: [PATCH 012/227] Adding SublimeText snippet for quick Backbone and CoffeeScript kickstart --- .../BackboneCoffee.sublime-snippet | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 SublimeTextSnippets/BackboneCoffee.sublime-snippet diff --git a/SublimeTextSnippets/BackboneCoffee.sublime-snippet b/SublimeTextSnippets/BackboneCoffee.sublime-snippet new file mode 100644 index 0000000..189f016 --- /dev/null +++ b/SublimeTextSnippets/BackboneCoffee.sublime-snippet @@ -0,0 +1,15 @@ + + + + + + +]]> + bbcoffee + From 6ee4e35588371cba13e7d3b03db76a79c9c0e1f5 Mon Sep 17 00:00:00 2001 From: Kostas Date: Tue, 7 Jul 2015 10:58:40 +0300 Subject: [PATCH 013/227] Adding Backbone object init check... --- SublimeTextSnippets/BackboneCoffee.sublime-snippet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SublimeTextSnippets/BackboneCoffee.sublime-snippet b/SublimeTextSnippets/BackboneCoffee.sublime-snippet index 189f016..318fdae 100644 --- a/SublimeTextSnippets/BackboneCoffee.sublime-snippet +++ b/SublimeTextSnippets/BackboneCoffee.sublime-snippet @@ -7,7 +7,7 @@ ]]> From 4d8453e994c5d7eff9f9c3a15ba738e47e90c53f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 7 Jul 2015 11:27:32 +0300 Subject: [PATCH 014/227] Adding BBCoffee ST snippet demo --- img/BBCoffee.gif | Bin 0 -> 955071 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/BBCoffee.gif diff --git a/img/BBCoffee.gif b/img/BBCoffee.gif new file mode 100644 index 0000000000000000000000000000000000000000..bca4780dc35127a985f8d8afe28dca46289709ce GIT binary patch literal 955071 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 z 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

>HEQlJmqSJz}5pPR%ok&E*}7=hwkA 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 z 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

&Mp9(%X9kV=DjXuS#K2;h! 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 literal 0 HcmV?d00001 From f6e6ac9e8baa3cbbb305e36cb7b9342fb31133bd Mon Sep 17 00:00:00 2001 From: Kostas Date: Thu, 9 Jul 2015 22:36:25 +0300 Subject: [PATCH 015/227] Adding Getting Started with Backbone --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4c3c914..4c11af8 100755 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ##### 3.4.1 BACKBONE.JS +* [**Getting Started with Backbone**, by Koren Leslie Cohen](http://www.korenlc.com/backbone-js-tutorial-getting-started-with-backbone/) * [**Awesome Backbone** List of Resources](https://github.com/instanceofpro/awesome-backbone/blob/master/README.md) * [Introduction to Backbone Js and Setting Up an Working Environment – Learning Backbone Js](http://www.codebeerstartups.com/2012/12/introduction-to-backbone-js-and-setting-up-an-working-environment/) * [Single Page ToDo Application With Backbone.js](http://code.tutsplus.com/tutorials/single-page-todo-application-with-backbonejs--cms-21417) From a5e4c4abc141929bb00e887112bc0159469ee30f Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 11 Jul 2015 12:12:01 +0300 Subject: [PATCH 016/227] Adding Essential JavaScript Links by Eric Elliot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4c11af8..74847c0 100755 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s #### 3.2 GENERAL +* [Essential JavaScript Links,A curated list by Eric Elliott and friends](https://github.com/ericelliott/essential-javascript-links) * [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/) * [Learn JavaScript Essentials (for all skill levels), by Eric Elliott](https://medium.com/javascript-scene/learn-javascript-b631a4af11f2) * [JavaScript, The Right Way](http://www.jstherightway.org/) From 7067a54f206773d1d374411c60797e818280c9f1 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 11 Jul 2015 16:55:55 +0300 Subject: [PATCH 017/227] Create Array.prototype.take.js --- snippets/Array.prototype.take.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 snippets/Array.prototype.take.js diff --git a/snippets/Array.prototype.take.js b/snippets/Array.prototype.take.js new file mode 100644 index 0000000..118ff2b --- /dev/null +++ b/snippets/Array.prototype.take.js @@ -0,0 +1,12 @@ +// Javascript implementation of Ruby Array::take | http://ruby-doc.org/core-2.2.0/Array.html#method-i-take + +if ( ! Array.prototype.take ){ + Array.prototype.take = function( n ){ + return this.slice( 0, n ); + } +} + +// USAGE: +// [1,2,3,4,5,6,7,8,9,0].take(3); +// #=> [1,2,3] + From 34a35cf86b900340e44d6901a01a0967da590e28 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 12 Jul 2015 23:29:49 +0300 Subject: [PATCH 018/227] Adding Functional Programming section --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 74847c0..eebd010 100755 --- a/README.md +++ b/README.md @@ -171,6 +171,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [MetaJS: Visualize JavaScript AST Execution](http://int3.github.io/metajs/) * [Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014](https://www.youtube.com/watch?v=8aGhZQkoFbQ) +> FUNCTIONAL PROGRAMMING + +* [Recursion in Functional JavaScript, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) + #### 3.3 NODE > BEGINNERS From d455eb08d4ffdd9f0fc5276b1294cdf47ad213d1 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 12 Jul 2015 23:33:35 +0300 Subject: [PATCH 019/227] Pure, functional JavaScript, Christian Johansen --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eebd010..80ddeb7 100755 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING * [Recursion in Functional JavaScript, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) +* [**VIDEO** | Pure, functional JavaScript, Christian Johansen](https://vimeo.com/43382919) #### 3.3 NODE From 3151e9bfb442461695960d0a0114abcf812a6b61 Mon Sep 17 00:00:00 2001 From: Kostas Date: Tue, 14 Jul 2015 12:26:26 +0300 Subject: [PATCH 020/227] Create JavaScript-Pad-Number.js --- snippets/JavaScript-Pad-Number.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 snippets/JavaScript-Pad-Number.js diff --git a/snippets/JavaScript-Pad-Number.js b/snippets/JavaScript-Pad-Number.js new file mode 100644 index 0000000..53534bc --- /dev/null +++ b/snippets/JavaScript-Pad-Number.js @@ -0,0 +1,6 @@ +var pad = function( number ) { + var r = String( number ); + return ( r.length === 1 ) ? '0' + r : r; +}; + +// USAGE: pad(1); --> 01, pad(20) --> 20 From bd25164520479641dbfbefe906c1417f8ce3302b Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 15 Jul 2015 10:48:04 +0300 Subject: [PATCH 021/227] Adding Getting Started with Backbone.js tutorial by Miguel Mota --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 80ddeb7..c0d7076 100755 --- a/README.md +++ b/README.md @@ -254,6 +254,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ##### 3.4.1 BACKBONE.JS +* [**Getting Started with Backbone.js**, By Miguel Mota](https://miguelmota.com/blog/getting-started-with-backbonejs/) * [**Getting Started with Backbone**, by Koren Leslie Cohen](http://www.korenlc.com/backbone-js-tutorial-getting-started-with-backbone/) * [**Awesome Backbone** List of Resources](https://github.com/instanceofpro/awesome-backbone/blob/master/README.md) * [Introduction to Backbone Js and Setting Up an Working Environment – Learning Backbone Js](http://www.codebeerstartups.com/2012/12/introduction-to-backbone-js-and-setting-up-an-working-environment/) From a1e2cfced4af461f56bb63f4e9834954a33c970f Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 15 Jul 2015 10:50:01 +0300 Subject: [PATCH 022/227] Adding YDKJS boo series --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c0d7076..f2bb9d3 100755 --- a/README.md +++ b/README.md @@ -318,6 +318,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s #### 3.5 BOOKS + * [**YDKJS: You-Dont-Know-JS**, A book series on JavaScript by Kyle Simpson](https://github.com/getify/You-Dont-Know-JS) * [JavaScript: The Good Parts, *by Douglas Crockford*](http://shop.oreilly.com/product/9780596517748.do) * [JavaScript, The Definitive Guide, *by David Flanagan*](http://shop.oreilly.com/product/9780596805531.do) * [High Performance JavaScript, *by Nicolas Zakas*](http://shop.oreilly.com/product/9780596802806.do) From dfab7fca550344c66765b38445aa1fa91a20ee20 Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 15 Jul 2015 10:54:15 +0300 Subject: [PATCH 023/227] Adding Functional Programming learning course Adding Functional Programming learning course by Jafar Husain. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f2bb9d3..3bcfb2b 100755 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING +* [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [Recursion in Functional JavaScript, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) * [**VIDEO** | Pure, functional JavaScript, Christian Johansen](https://vimeo.com/43382919) From 4ecb7a314075e51b9eef93f28605da212dbb158f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 18 Jul 2015 12:25:59 +0300 Subject: [PATCH 024/227] Adding a couple of snippets. --- snippets/JavaScript-Pad-Milliseconds.coffee | 6 ++++++ snippets/JavaScript-Pad-Milliseconds.js | 7 +++++++ snippets/Node-Parse-File-By-Line.coffee | 17 +++++++++++++++++ snippets/RegExp-Credit-Cards.js | 7 +++++++ snippets/RegExp-Parse-URL.js | 15 +++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 snippets/JavaScript-Pad-Milliseconds.coffee create mode 100644 snippets/JavaScript-Pad-Milliseconds.js create mode 100644 snippets/Node-Parse-File-By-Line.coffee create mode 100644 snippets/RegExp-Credit-Cards.js create mode 100644 snippets/RegExp-Parse-URL.js diff --git a/snippets/JavaScript-Pad-Milliseconds.coffee b/snippets/JavaScript-Pad-Milliseconds.coffee new file mode 100644 index 0000000..5556991 --- /dev/null +++ b/snippets/JavaScript-Pad-Milliseconds.coffee @@ -0,0 +1,6 @@ +# JavaScript-Pad-Milliseconds + +padMs = (num)-> + + if num < 10 then '00' + String(num) else if num < 100 then '0' + String(num) else String(num) + diff --git a/snippets/JavaScript-Pad-Milliseconds.js b/snippets/JavaScript-Pad-Milliseconds.js new file mode 100644 index 0000000..a328494 --- /dev/null +++ b/snippets/JavaScript-Pad-Milliseconds.js @@ -0,0 +1,7 @@ +// JavaScript-Pad-Milliseconds + +function padMs ( num ) { + + return ( num < 10 ) ? '00' + String( num ) : ( num < 100 ) ? '0' + String( num ) : String( num ); + +} diff --git a/snippets/Node-Parse-File-By-Line.coffee b/snippets/Node-Parse-File-By-Line.coffee new file mode 100644 index 0000000..cd60d46 --- /dev/null +++ b/snippets/Node-Parse-File-By-Line.coffee @@ -0,0 +1,17 @@ +fs = require('fs') +readline = require('readline') +filename = '/path/to/file' + +read = readline.createInterface( + input: fs.createReadStream(filename) + output: process.stdout + terminal: false) + +read.on 'line', (line) -> + console.log line + +read.on 'close', -> + console.log 'Finished parsing ' + filename + +### http://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js ### + diff --git a/snippets/RegExp-Credit-Cards.js b/snippets/RegExp-Credit-Cards.js new file mode 100644 index 0000000..7a960b5 --- /dev/null +++ b/snippets/RegExp-Credit-Cards.js @@ -0,0 +1,7 @@ +^(?:4[0-9]{12}(?:[0-9]{3})? # VISA +| 5[1-5][0-9]{14} # MasterCard +| 3[47][0-9]{13} # AMEX +| 3(?:0[0-5]|[68][0-9])[0-9]{11} # Diners +| 6(?:011|5[0-9]{2})[0-9]{12} +| (?:2131|1800|35\d{3})\d{11} # JCB +)$ \ No newline at end of file diff --git a/snippets/RegExp-Parse-URL.js b/snippets/RegExp-Parse-URL.js new file mode 100644 index 0000000..3ef55fd --- /dev/null +++ b/snippets/RegExp-Parse-URL.js @@ -0,0 +1,15 @@ +( + ( // brackets covering match for protocol (optional) and domain + ([A-Za-z]{3,9}:(?:\/\/)?) // match protocol, allow in format http:// or mailto: + (?:[\-;:&=\+\$,\w]+@)? // allow something@ for email addresses + [A-Za-z0-9\.\-]+ // anything looking at all like a domain, non-unicode domains + | // or instead of above + (?:www\.|[\-;:&=\+\$,\w]+@) // starting with something@ or www. + [A-Za-z0-9\.\-]+ // anything looking at all like a domain + ) + ( // brackets covering match for path, query string and anchor + (?:\/[\+~%\/\.\w\-]*) // allow optional /path + ?\??(?:[\-\+=&;%@\.\w]*) // allow optional query string starting with ? + #?(?:[\.\!\/\\\w]*) // allow optional anchor #anchor + )? // make URL suffix optional +) \ No newline at end of file From 4b44a7903650ca43ddf6a4f3e74155d6777095f5 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 19 Jul 2015 00:04:43 +0300 Subject: [PATCH 025/227] Adding CoffeeScript resources. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3bcfb2b..08e8dff 100755 --- a/README.md +++ b/README.md @@ -447,8 +447,10 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c > BOOKS - * [Smooth CoffeeScript](http://autotelicum.github.io/Smooth-CoffeeScript/) - * [The Little Book on CoffeeScript](http://arcturo.github.io/library/coffeescript/) + * [**CoffeeScript Ristretto**](https://leanpub.com/coffeescript-ristretto/read) + * [**Smooth CoffeeScript**](http://autotelicum.github.io/Smooth-CoffeeScript/) + * [**The Little Book on CoffeeScript**](http://arcturo.github.io/library/coffeescript/) + * [**CoffeeScript: The Good Parts**, By Azat Mardan](https://qconnewyork.com/ny2014/system/files/presentation-slides/CoffeeScript-The-Good-Parts.pdf) ------ From d7b4ba6d2ca1cc673f175e38bfd63dc70f11b717 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 19 Jul 2015 01:11:06 +0300 Subject: [PATCH 026/227] Adding section on Promises --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 08e8dff..b7cccf9 100755 --- a/README.md +++ b/README.md @@ -151,6 +151,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) +> PROMISES + +* [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) + > DEBUGGING * [Debugging JavaScript](https://developer.chrome.com/devtools/docs/javascript-debugging) From ceaffd5ac5cf1d3b14309c66d23fbe01cba60645 Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 22 Jul 2015 10:50:56 +0300 Subject: [PATCH 027/227] Adding CoffeePad --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b7cccf9..4d9b509 100755 --- a/README.md +++ b/README.md @@ -449,6 +449,10 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c * [CoffeeScript official site](http://coffeescript.org/) +> TOOLS + +* [**CoffeePad**: CoffeeScript editor in your browser](https://github.com/gokmen/coffeepad) + > BOOKS * [**CoffeeScript Ristretto**](https://leanpub.com/coffeescript-ristretto/read) From f8381c851fd27d25fc0c65c4d0e274554dde3b1a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 23 Jul 2015 19:02:12 +0300 Subject: [PATCH 028/227] Adding Sleep, FileWrite and Percentage snippets. --- snippets/JavaScript-Sleep.js | 7 +++++++ snippets/Node-Write-File.coffee | 8 ++++++++ snippets/RegExp-Percentage.js | 2 ++ 3 files changed, 17 insertions(+) create mode 100644 snippets/JavaScript-Sleep.js create mode 100644 snippets/Node-Write-File.coffee create mode 100644 snippets/RegExp-Percentage.js diff --git a/snippets/JavaScript-Sleep.js b/snippets/JavaScript-Sleep.js new file mode 100644 index 0000000..a0c516b --- /dev/null +++ b/snippets/JavaScript-Sleep.js @@ -0,0 +1,7 @@ +!window.sleep && (window.sleep = function sleep(secs){ + + for ( var end = Date.now() + secs * 1000; Date.now() < end; ); + +}); + +// USAGE: console.log("start..."); sleep(3); console.log("...finish"); diff --git a/snippets/Node-Write-File.coffee b/snippets/Node-Write-File.coffee new file mode 100644 index 0000000..3f7969e --- /dev/null +++ b/snippets/Node-Write-File.coffee @@ -0,0 +1,8 @@ +fs = require 'fs' + +fs.writeFile "/tmp/test", "Hey there!", (err)-> + + if (err) + console.log err + else + console.log "The file was saved!" diff --git a/snippets/RegExp-Percentage.js b/snippets/RegExp-Percentage.js new file mode 100644 index 0000000..b8c67e6 --- /dev/null +++ b/snippets/RegExp-Percentage.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = /^(\d+(\.\d+)?|\.\d+) ?%$/; From f8d187dca5f248a5d437b1a6646102147e63844f Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 25 Jul 2015 20:51:43 +0300 Subject: [PATCH 029/227] Adding Top 8 Common Backbone.js Developer Mistakes --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4d9b509..da6a570 100755 --- a/README.md +++ b/README.md @@ -269,6 +269,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Backbone.js Video Tutorials on YouTube, by *Moshfegh Hamedani*](https://www.youtube.com/watch?v=4t0n5k0X7ow) * [Backbone.js Tutorial for Beginners, by *Thomas Davis*](https://www.youtube.com/watch?v=FZSjvWtUxYk) * [Sample application built with Backbone.js, Twitter Bootstrap, Node.js, Express, MongoDB, by *Christophe Coenraets*](https://github.com/ccoenraets/nodecellar) +* [Top 8 Common Backbone.js Developer Mistakes, By Mahmub Ridwan](http://www.toptal.com/backbone-js/top-8-common-backbone-js-developer-mistakes) > CODE From febf469234137093cce5d48976f47108375de3c3 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 25 Aug 2015 22:03:41 +0200 Subject: [PATCH 030/227] Adding Functional Programming video link and resource for online code editors --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4d9b509..e9b9a8b 100755 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [JSBin](http://jsbin.com/) * [JSFiddle](http://jsfiddle.net/) +• [16 ONLINE JS EDITORS](http://codecondo.com/16-online-javascript-editors-for-web-developers/) > APIs @@ -180,6 +181,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [Recursion in Functional JavaScript, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) * [**VIDEO** | Pure, functional JavaScript, Christian Johansen](https://vimeo.com/43382919) +* [**VIDEO** | Lenses Quick n' Dirty: Functional Lenses in javascript. Implementation and some use cases, By Brian Lonsdorf](https://vimeo.com/104807358) #### 3.3 NODE From e72781a9067868f1ae113a098d498033ef538610 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 27 Aug 2015 15:33:17 +0200 Subject: [PATCH 031/227] Updating Functional Programming section --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 610f7cd..7ed3b8f 100755 --- a/README.md +++ b/README.md @@ -176,12 +176,19 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [MetaJS: Visualize JavaScript AST Execution](http://int3.github.io/metajs/) * [Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014](https://www.youtube.com/watch?v=8aGhZQkoFbQ) -> FUNCTIONAL PROGRAMMING +> FUNCTIONAL PROGRAMMING: **READING** * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) -* [Recursion in Functional JavaScript, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) -* [**VIDEO** | Pure, functional JavaScript, Christian Johansen](https://vimeo.com/43382919) -* [**VIDEO** | Lenses Quick n' Dirty: Functional Lenses in javascript. Implementation and some use cases, By Brian Lonsdorf](https://vimeo.com/104807358) +* [**Recursion in Functional JavaScript**, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) + +> FUNCTIONAL PROGRAMMING: **VIDEO** + +* [**Next-level functional Javascript with Ramda**, By Warren Seymour](https://vimeo.com/129549453) +* [**Pure, functional JavaScript**, By Christian Johansen](https://vimeo.com/43382919) +* [**Lenses Quick n' Dirty: Functional Lenses in javascript**, By Brian Lonsdorf](https://vimeo.com/104807358) + +> FUNCTIONAL PROGRAMMING: **LIBRARIES** + #### 3.3 NODE From e9479808c1a66a3baa4bb71c024ceef3bb72fbdc Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 27 Aug 2015 15:35:44 +0200 Subject: [PATCH 032/227] Adding FP libraries links --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7ed3b8f..e0975d3 100755 --- a/README.md +++ b/README.md @@ -189,6 +189,9 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **LIBRARIES** +* [*Ramda.JS*](http://ramdajs.com/) +* [*Lodash.JS*](https://lodash.com/) +* [*Underscore.JS*](http://underscorejs.org/) #### 3.3 NODE From 188d902395ad47b6dd2bfcc23eacb5c7994c45e1 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 27 Aug 2015 15:36:18 +0200 Subject: [PATCH 033/227] Fixed typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e0975d3..e16b5dc 100755 --- a/README.md +++ b/README.md @@ -189,9 +189,9 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **LIBRARIES** -* [*Ramda.JS*](http://ramdajs.com/) -* [*Lodash.JS*](https://lodash.com/) -* [*Underscore.JS*](http://underscorejs.org/) +* [**Ramda.JS**](http://ramdajs.com/) +* [**Lodash.JS**](https://lodash.com/) +* [**Underscore.JS**](http://underscorejs.org/) #### 3.3 NODE From bc208437d76d1e0ad21d6911e2c455c659a23502 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 28 Aug 2015 10:26:38 +0200 Subject: [PATCH 034/227] Added Functional Programming link on Currying --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e16b5dc..194639d 100755 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **READING** +* [**Why Curry Helps**, By Hugh FD Jackson](https://hughfdjackson.com/javascript/why-curry-helps/) * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [**Recursion in Functional JavaScript**, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) From 81df5805ae587481d4bac4f873d0d4dff8b0aa4c Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 30 Aug 2015 17:20:19 +0200 Subject: [PATCH 035/227] Added 'How to Become a Great JavaScript Developer' post. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 194639d..0ba0501 100755 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [45 Useful JavaScript Tips, Tricks and Best Practices](http://modernweb.com/2013/12/23/45-useful-javascript-tips-tricks-and-best-practices/) * [JavaScript DevDocs](http://devdocs.io/javascript/) * [MDN JavaScript Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) +* [**How to Become a Great JavaScript Developer***](http://blog.ustunozgur.com/javascript/programming/books/videos/2015/06/17/how_to_be_a_great_javascript_software_developer.html) > COURSES From b1eecd5417489a448f0e7cf3b202f86ab8305c93 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 5 Sep 2015 02:01:38 +0200 Subject: [PATCH 036/227] adding Functional Programming by Scott Sauyet --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0ba0501..6b57082 100755 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**Why Curry Helps**, By Hugh FD Jackson](https://hughfdjackson.com/javascript/why-curry-helps/) * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [**Recursion in Functional JavaScript**, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) +* [**Functional Programming**, By Scott Sauyet](http://scott.sauyet.com/Javascript/Talk/FunctionalProgramming/) > FUNCTIONAL PROGRAMMING: **VIDEO** From d317d7967cd79701648619202095fce8735b5c1f Mon Sep 17 00:00:00 2001 From: Kostas Date: Mon, 14 Sep 2015 22:23:40 +0200 Subject: [PATCH 037/227] Added interactive Node courses Added Node interactive courses test-anything and stream-adventure --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6b57082..a882959 100755 --- a/README.md +++ b/README.md @@ -200,6 +200,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > BEGINNERS +* [**Stream Adventure:** Go on an educational stream adventure!](https://github.com/substack/stream-adventure) +* [**test-anything:** Learn to test anything with TAP](https://github.com/finnp/test-anything) * [Node.js for Beginnersby Maciej Sopyło](http://code.tutsplus.com/tutorials/node-js-for-beginners--net-26314) * [Node School](http://nodeschool.io/) * [Resources to Get You Up to Speed in Node.js](http://code.tutsplus.com/articles/resources-to-get-you-up-to-speed-in-nodejs--cms-21431) From da564d9fc9c0d8df8462a7ddbb2881119adbcb73 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 20 Sep 2015 13:45:17 +0200 Subject: [PATCH 038/227] Adding Hard Rock CoffeeScript --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a882959..c14a3d5 100755 --- a/README.md +++ b/README.md @@ -465,7 +465,8 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c ##### 3.11.1 COFFEESCRIPT - * [CoffeeScript official site](http://coffeescript.org/) + * [**CoffeeScript** Official site](http://coffeescript.org/) + * [**Hard Rock CoffeeScript**](http://hardrockcoffeescript.org/) > TOOLS From 6580e7760f81a30a83610a5046a5855781787eee Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 20 Sep 2015 15:29:20 +0200 Subject: [PATCH 039/227] Added CoffeeScript: The beautiful way to write JavaScript --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c14a3d5..64b8112 100755 --- a/README.md +++ b/README.md @@ -467,6 +467,7 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c * [**CoffeeScript** Official site](http://coffeescript.org/) * [**Hard Rock CoffeeScript**](http://hardrockcoffeescript.org/) + * [**CoffeeScript: The beautiful way to write JavaScript**, By Amir Salihefendic](http://amix.dk/blog/post/19612) > TOOLS From d1173cbd5cd7c154355c29c03e864fd4c5ef0745 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sun, 20 Sep 2015 15:51:21 +0200 Subject: [PATCH 040/227] Adding Ramda REPL --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 64b8112..df7c252 100755 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [**Recursion in Functional JavaScript**, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) * [**Functional Programming**, By Scott Sauyet](http://scott.sauyet.com/Javascript/Talk/FunctionalProgramming/) +* [Ramda + ramda-fantasy REPL](https://github.com/hemanth/ramda-repl) > FUNCTIONAL PROGRAMMING: **VIDEO** From 9b1f87038d1850e96bead2f5880ecaa7c28ebbbd Mon Sep 17 00:00:00 2001 From: Kostas Date: Thu, 24 Sep 2015 23:03:26 +0200 Subject: [PATCH 041/227] Adding Taking JavaScript Seriously with Backbone.js Adding Taking JavaScript Seriously with Backbone.js video by Jeremy Ashkenas --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index df7c252..3f5dc6c 100755 --- a/README.md +++ b/README.md @@ -284,8 +284,6 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Single Page ToDo Application With Backbone.js](http://code.tutsplus.com/tutorials/single-page-todo-application-with-backbonejs--cms-21417) * [Developing Backbone.js Applications, *by Addy Osmani*](https://github.com/addyosmani/backbone-fundamentals) * [Your First Backbone.js App – Service Chooser, *by Martin Angelov*](http://tutorialzine.com/2013/04/services-chooser-backbone-js/) -* [Backbone.js Video Tutorials on YouTube, by *Moshfegh Hamedani*](https://www.youtube.com/watch?v=4t0n5k0X7ow) -* [Backbone.js Tutorial for Beginners, by *Thomas Davis*](https://www.youtube.com/watch?v=FZSjvWtUxYk) * [Sample application built with Backbone.js, Twitter Bootstrap, Node.js, Express, MongoDB, by *Christophe Coenraets*](https://github.com/ccoenraets/nodecellar) * [Top 8 Common Backbone.js Developer Mistakes, By Mahmub Ridwan](http://www.toptal.com/backbone-js/top-8-common-backbone-js-developer-mistakes) @@ -297,6 +295,12 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Developing Backbone.js Applications, *By Addy Osmani*](http://addyosmani.github.io/backbone-fundamentals/) +> VIDEOS + +* [Backbone.js Video Tutorials on YouTube, by *Moshfegh Hamedani*](https://www.youtube.com/watch?v=4t0n5k0X7ow) +* [Backbone.js Tutorial for Beginners, by *Thomas Davis*](https://www.youtube.com/watch?v=FZSjvWtUxYk) +* [**Jeremy Ashkenas - Taking JavaScript Seriously with Backbone.js**](https://www.youtube.com/watch?v=4udR30JYenA) + ##### 3.4.2 JQUERY * [A tidy repository of jQuery plugins, *by @psnka*](http://unheap.com/) From 74496d27f2fe75c3e908daefee22aa69c519dd31 Mon Sep 17 00:00:00 2001 From: Kostas Date: Tue, 6 Oct 2015 16:13:08 +0200 Subject: [PATCH 042/227] Adding Rise of the Transpilers video --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3f5dc6c..02da09f 100755 --- a/README.md +++ b/README.md @@ -478,6 +478,10 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c * [**CoffeePad**: CoffeeScript editor in your browser](https://github.com/gokmen/coffeepad) +> VIDEOS + +* [**Rise of the Transpilers** by Jeremy Ashkenas](https://www.youtube.com/watch?v=DspYurD75Ns) + > BOOKS * [**CoffeeScript Ristretto**](https://leanpub.com/coffeescript-ristretto/read) From aeef5509878473f486ac4a04ccd216c25bbf07de Mon Sep 17 00:00:00 2001 From: Kostas Date: Mon, 12 Oct 2015 18:33:06 +0200 Subject: [PATCH 043/227] Functional programming & curry cooking in JS video Added Functional programming and curry cooking in JS** By Stefanie Schirmer --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 02da09f..ae68a4e 100755 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **VIDEO** +* [**Functional programming and curry cooking in JS** By Stefanie Schirmer | JSConf EU 2015**](https://www.youtube.com/watch?v=6Qx5ZAbfqjo) * [**Next-level functional Javascript with Ramda**, By Warren Seymour](https://vimeo.com/129549453) * [**Pure, functional JavaScript**, By Christian Johansen](https://vimeo.com/43382919) * [**Lenses Quick n' Dirty: Functional Lenses in javascript**, By Brian Lonsdorf](https://vimeo.com/104807358) From c7f1cd96e2c4c65933c8f092c4e21fb2464f2d23 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 17 Oct 2015 01:22:49 +0200 Subject: [PATCH 044/227] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ae68a4e..6562921 100755 --- a/README.md +++ b/README.md @@ -91,6 +91,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") +#### ECMASCRIPT 6 + +[**Overview of ECMAScript 6 features**](https://github.com/lukehoban/es6features) + ### 3. RESOURCES #### 3.1 BEGINNER RESOURCES From 27a1560b9474c4457155851386480dc639af2bc1 Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 21 Oct 2015 21:43:21 +0200 Subject: [PATCH 045/227] Async and OLOO references --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6562921..5dde0fe 100755 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Understand type coercion * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) -* Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/) -* Understand `callbacks`, `IIFEs` and `asynchronicity` +* Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) +* Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. Here are 2 great videos on the subject by Paul Irish: @@ -157,8 +157,9 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) -> PROMISES +> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT +* [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) > DEBUGGING From 9dce06bf7ae82358db399009723217332bc4d220 Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 21 Oct 2015 21:44:48 +0200 Subject: [PATCH 046/227] Adding HREF link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dde0fe..9d19607 100755 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) -> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT +> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT * [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) From e7e5f7a44fa78c19f5120764da2cdd408ba77f1d Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 21 Oct 2015 21:45:56 +0200 Subject: [PATCH 047/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d19607..077e5be 100755 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) -> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT +> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT * [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) From cf986f051492e468dd58134288865062cdbd2dee Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 21 Oct 2015 21:47:52 +0200 Subject: [PATCH 048/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 077e5be..dedac21 100755 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) -> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT +> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT * [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) From 8df5f0b239b4c0262b754648ce6c6e024190913c Mon Sep 17 00:00:00 2001 From: Kostas Date: Wed, 21 Oct 2015 21:50:28 +0200 Subject: [PATCH 049/227] Async Links added --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dedac21..54816d6 100755 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) +* [**The Evolution of Asynchronous JavaScript**](https://blog.risingstack.com/asynchronous-javascript/) +* [**Javascript Async Control Flow**, By KENNY KAYE | 20 OCTOBER 2015](https://kaye.us/javascript-async-control-flow/) > DEBUGGING From 17e07b7ecab296439492a7f7d6ea990e7ef38349 Mon Sep 17 00:00:00 2001 From: Kostas Date: Fri, 23 Oct 2015 01:58:19 +0200 Subject: [PATCH 050/227] Added "ES6 Overview in 350 Bullet Points" post --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54816d6..80dbe20 100755 --- a/README.md +++ b/README.md @@ -91,9 +91,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") -#### ECMASCRIPT 6 +#### ECMASCRIPT 6 | ES2015 -[**Overview of ECMAScript 6 features**](https://github.com/lukehoban/es6features) +* [**ES6 Overview in 350 Bullet Points**](https://ponyfoo.com/articles/es6) +* [**Overview of ECMAScript 6 features**](https://github.com/lukehoban/es6features) ### 3. RESOURCES From f5e931a4a73bac7db37a361b801732db634ca8e2 Mon Sep 17 00:00:00 2001 From: Kostas Date: Sat, 24 Oct 2015 22:47:40 +0200 Subject: [PATCH 051/227] Added "An Intro to Functional Programming Concepts in JavaScript" --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 80dbe20..53be884 100755 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **READING** +* [**An Intro to Functional Programming Concepts in JavaScript**, By Thomas Collardeau ](https://medium.com/@collardeau/intro-to-functional-programming-concepts-in-javascript-b0650773139c#.8bp7dkyvm) * [**Why Curry Helps**, By Hugh FD Jackson](https://hughfdjackson.com/javascript/why-curry-helps/) * [**Functional Programming in Javascript** An interactive learning course by Jafar Husain](http://jhusain.github.io/learnrx/) * [**Recursion in Functional JavaScript**, By M. David Green](http://www.sitepoint.com/recursion-functional-javascript/) From 6f6e41ec52a7c84fc5d7b8c6044393f75b650e12 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 25 Dec 2015 12:37:44 +0100 Subject: [PATCH 052/227] Adding snippet that removes falsy values from Array --- snippets/JavaScript-Array-Remove-Falsy-Values.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 snippets/JavaScript-Array-Remove-Falsy-Values.js diff --git a/snippets/JavaScript-Array-Remove-Falsy-Values.js b/snippets/JavaScript-Array-Remove-Falsy-Values.js new file mode 100644 index 0000000..0c87350 --- /dev/null +++ b/snippets/JavaScript-Array-Remove-Falsy-Values.js @@ -0,0 +1,6 @@ +// REMOVE ALL FALSY VALUES FROM AN ARRAY +// Source: http://jsforallof.us/2014/10/03/cool-es5-array-method-uses/ + +var arr = ["", 1, "foo", 0, "bar"]; +console.log(arr.filter(Boolean)); // [1, "foo", "bar"] + From 22adc4883d0213b964eb681fbe82d71f571c0594 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 25 Dec 2015 12:40:23 +0100 Subject: [PATCH 053/227] Adding snippet for Zip Archiver --- snippets/Node-Zip.coffee | 42 ++++++++++++++++++++++++++++++++++++++++ snippets/Node-Zip.js | 31 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 snippets/Node-Zip.coffee create mode 100644 snippets/Node-Zip.js diff --git a/snippets/Node-Zip.coffee b/snippets/Node-Zip.coffee new file mode 100644 index 0000000..6d842cc --- /dev/null +++ b/snippets/Node-Zip.coffee @@ -0,0 +1,42 @@ +# npm install adm-zip + +AdmZip = require('adm-zip') + +#=== READING ARCHIVES + +zip = new AdmZip('./my_file.zip') +zipEntries = zip.getEntries() + +# AN ARRAY OF ZIPENTRY RECORDS + +zipEntries.forEach (zipEntry) -> + + console.log zipEntry.toString() + if zipEntry.entryName == 'my_file.txt' + console.log zipEntry.data.toString('utf8') # outputs zip entries information + return + +# OUTPUTS THE CONTENT OF SOME_FOLDER/MY_FILE.TXT +console.log zip.readAsText('some_folder/my_file.txt') + +# EXTRACTS THE SPECIFIED FILE TO THE SPECIFIED LOCATION +zip.extractEntryTo 'some_folder/my_file.txt', '/home/me/tempfolder', false, true + +# EXTRACTS EVERYTHING +zip.extractAllTo '/home/me/zipcontent/', true + + +#=== CREATING ARCHIVES + +zip = new AdmZip + +# ADD FILE DIRECTLY +zip.addFile 'test.txt', new Buffer('inner content of the file'), 'entry comment goes here' + +# ADD LOCAL FILE +zip.addLocalFile '/home/me/some_picture.png' + +# GET EVERYTHING AS A BUFFER +willSendthis = zip.toBuffer() +# OR WRITE EVERYTHING TO DISK +zip.writeZip '/home/me/files.zip' diff --git a/snippets/Node-Zip.js b/snippets/Node-Zip.js new file mode 100644 index 0000000..32cd91b --- /dev/null +++ b/snippets/Node-Zip.js @@ -0,0 +1,31 @@ +// Generated by CoffeeScript 1.9.3 +var AdmZip, willSendthis, zip, zipEntries; + +AdmZip = require('adm-zip'); + +zip = new AdmZip('./my_file.zip'); + +zipEntries = zip.getEntries(); + +zipEntries.forEach(function(zipEntry) { + console.log(zipEntry.toString()); + if (zipEntry.entryName === 'my_file.txt') { + console.log(zipEntry.data.toString('utf8')); + } +}); + +console.log(zip.readAsText('some_folder/my_file.txt')); + +zip.extractEntryTo('some_folder/my_file.txt', '/home/me/tempfolder', false, true); + +zip.extractAllTo('/home/me/zipcontent/', true); + +zip = new AdmZip; + +zip.addFile('test.txt', new Buffer('inner content of the file'), 'entry comment goes here'); + +zip.addLocalFile('/home/me/some_picture.png'); + +willSendthis = zip.toBuffer(); + +zip.writeZip('/home/me/files.zip'); From e868f7bb930f901d0ecf6f844caec26b879bcc86 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 25 Dec 2015 12:42:43 +0100 Subject: [PATCH 054/227] Adding snippet to download files with Node --- snippets/Node-Download-File.coffee | 25 +++++++++++++++++++++++++ snippets/Node-Download-File.js | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 snippets/Node-Download-File.coffee create mode 100644 snippets/Node-Download-File.js diff --git a/snippets/Node-Download-File.coffee b/snippets/Node-Download-File.coffee new file mode 100644 index 0000000..5f958d7 --- /dev/null +++ b/snippets/Node-Download-File.coffee @@ -0,0 +1,25 @@ +http = require('https') +fs = require('fs') + +download = (url, dest, cb) -> + file = fs.createWriteStream(dest) + request = http.get(url, (response) -> + console.log "Downloading..." + response.pipe file + file.on 'finish', -> + file.close cb + console.log "Download finished." + # close() is async, call cb after close completes. + return + return + ).on('error', (err) -> + # Handle errors + fs.unlink dest + # Delete the file async. (But we don't check the result) + if cb + cb err.message + return + ) + return + +download "https://wordpress.org/latest.zip", "./latest.zip" \ No newline at end of file diff --git a/snippets/Node-Download-File.js b/snippets/Node-Download-File.js new file mode 100644 index 0000000..3843a82 --- /dev/null +++ b/snippets/Node-Download-File.js @@ -0,0 +1,26 @@ +// Generated by CoffeeScript 1.9.3 +var download, fs, http; + +http = require('https'); + +fs = require('fs'); + +download = function(url, dest, cb) { + var file, request; + file = fs.createWriteStream(dest); + request = http.get(url, function(response) { + console.log("Downloading..."); + response.pipe(file); + file.on('finish', function() { + file.close(cb); + console.log("Download finished."); + }); + }).on('error', function(err) { + fs.unlink(dest); + if (cb) { + cb(err.message); + } + }); +}; + +download("https://wordpress.org/latest.zip", "./latest.zip"); From 57fb9dfed39b43dbc8f9ed7d89bdde3ed6fb261d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 25 Dec 2015 13:45:08 +0100 Subject: [PATCH 055/227] Adding Date to UNIX Timestamp conversion snippet --- snippets/JavaScript-Date-Convert-to-UNIX-Timestamp.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 snippets/JavaScript-Date-Convert-to-UNIX-Timestamp.js diff --git a/snippets/JavaScript-Date-Convert-to-UNIX-Timestamp.js b/snippets/JavaScript-Date-Convert-to-UNIX-Timestamp.js new file mode 100644 index 0000000..41b7727 --- /dev/null +++ b/snippets/JavaScript-Date-Convert-to-UNIX-Timestamp.js @@ -0,0 +1,3 @@ +var date = new Date( "2012-01-01T06:25:24Z" ).getTime() / 1000; + +// Source: http://stackoverflow.com/questions/11893083/convert-normal-date-to-unix-timestamp From f2d7634d1d784110c3b88d4bb36f3c7abf652ee5 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 25 Dec 2015 13:46:53 +0100 Subject: [PATCH 056/227] Adding URL query string parser snippet --- snippets/JavaScript-Get-URL-Hash-Parameters.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 snippets/JavaScript-Get-URL-Hash-Parameters.js diff --git a/snippets/JavaScript-Get-URL-Hash-Parameters.js b/snippets/JavaScript-Get-URL-Hash-Parameters.js new file mode 100644 index 0000000..02d6856 --- /dev/null +++ b/snippets/JavaScript-Get-URL-Hash-Parameters.js @@ -0,0 +1,12 @@ +// -> Object +helpers.getHashParams = function() { + var hashParams = {}; + var e, + a = /\+/g, // Regex for replacing addition symbol with a space + r = /([^&;=]+)=?([^&;]*)/g, + d = function (s) { return decodeURIComponent(s.replace(a, " ")); }, + q = window.location.hash.substring(1); + while (e = r.exec(q)) + hashParams[d(e[1])] = d(e[2]); + return hashParams; +}; \ No newline at end of file From bc6cfd86f5a17661bdfa3a2cc9b6ceb3b798efe2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 7 Jan 2016 17:06:36 +0100 Subject: [PATCH 057/227] Adding snippet that detects whether script was called directly via Node or via require() --- .../Node-Detect-Module-Execution-Context.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 snippets/Node-Detect-Module-Execution-Context.js diff --git a/snippets/Node-Detect-Module-Execution-Context.js b/snippets/Node-Detect-Module-Execution-Context.js new file mode 100644 index 0000000..2a3c9b5 --- /dev/null +++ b/snippets/Node-Detect-Module-Execution-Context.js @@ -0,0 +1,18 @@ +/* +DESCRIPTION: DETECT IF CODE IS RUN DIRECTLY VIA node OR VIA require() +AUTHOR: https://github.com/pbrandt1 +*/ + +if ( ! module.parent ) { + + // ran with `node something.js` + app.listen(8088, function() { + console.log('app listening on port 8088'); + }) + +} else { + + // used with `require('/.something.js')` + module.exports = app; + +} \ No newline at end of file From acce1ec6d4016fa611e72c139ad79381af42f043 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 11:33:49 +0100 Subject: [PATCH 058/227] Adding String Reverse method --- snippets/JavaScript-String-Reverse-Method.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 snippets/JavaScript-String-Reverse-Method.js diff --git a/snippets/JavaScript-String-Reverse-Method.js b/snippets/JavaScript-String-Reverse-Method.js new file mode 100644 index 0000000..3e09dae --- /dev/null +++ b/snippets/JavaScript-String-Reverse-Method.js @@ -0,0 +1,5 @@ +String.prototype.reverse = function() { + + return Array.prototype.reverse.apply(this.split('')).join(''); + +}; From 0952a0c2591d70be47322169c3aa54aa01552ba8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 12:12:25 +0100 Subject: [PATCH 059/227] Added Functional Programming video on Recursion --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 53be884..927ac2d 100755 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > FUNCTIONAL PROGRAMMING: **VIDEO** +* [**Intro to Recursion - Refactoring to a Pure Function**, By Shanon Osbourne](https://egghead.io/lessons/javascript-intro-to-recursion-the-problem) * [**Functional programming and curry cooking in JS** By Stefanie Schirmer | JSConf EU 2015**](https://www.youtube.com/watch?v=6Qx5ZAbfqjo) * [**Next-level functional Javascript with Ramda**, By Warren Seymour](https://vimeo.com/129549453) * [**Pure, functional JavaScript**, By Christian Johansen](https://vimeo.com/43382919) From 58fc399969f2f78aa260d5c54be453533e8a4674 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 16:48:18 +0100 Subject: [PATCH 060/227] Adding Node Request snippets --- snippets/Node-Request.coffee | 19 +++++++++++++++++++ snippets/Node-Request.js | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 snippets/Node-Request.coffee create mode 100644 snippets/Node-Request.js diff --git a/snippets/Node-Request.coffee b/snippets/Node-Request.coffee new file mode 100644 index 0000000..d65f87c --- /dev/null +++ b/snippets/Node-Request.coffee @@ -0,0 +1,19 @@ +# npm install request + +request = require "request" + +request 'http://www.google.com', (error, response, body)-> + + if ( !error and response.statusCode is 200 ) then console.log body + +# REQUEST WITH OPTIONS +options = + url : "http://google.com" + qs : {} + +request options, (error, response, body)-> + + +# PIPING TO A FILE +destination = fs.createWriteStream './google.html' +request( "http://google.com" ).pipe(destination) diff --git a/snippets/Node-Request.js b/snippets/Node-Request.js new file mode 100644 index 0000000..42132b3 --- /dev/null +++ b/snippets/Node-Request.js @@ -0,0 +1,21 @@ +// Generated by CoffeeScript 1.10.0 +var destination, options, request; + +request = require("request"); + +request('http://www.google.com', function(error, response, body) { + if (!error && response.statusCode === 200) { + return console.log(body); + } +}); + +options = { + url: "http://google.com", + qs: {} +}; + +request(options, function(error, response, body) {}); + +destination = fs.createWriteStream('./google.html'); + +request("http://google.com").pipe(destination); From 63de0c8616dfa3232ef18fa0f6b1718f1acb264b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 17:14:03 +0100 Subject: [PATCH 061/227] Adding simple Cheerio and Request example --- snippets/Node-Cheerio-Simple-Scraping.coffee | 16 ++++++++++++++++ snippets/Node-Cheerio-Simple-Scraping.js | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 snippets/Node-Cheerio-Simple-Scraping.coffee create mode 100644 snippets/Node-Cheerio-Simple-Scraping.js diff --git a/snippets/Node-Cheerio-Simple-Scraping.coffee b/snippets/Node-Cheerio-Simple-Scraping.coffee new file mode 100644 index 0000000..003c0fa --- /dev/null +++ b/snippets/Node-Cheerio-Simple-Scraping.coffee @@ -0,0 +1,16 @@ +request = require 'request' +cheerio = require 'cheerio' + +url = 'http://www.company.com/jobs/' + +request url, (err, resp, body) -> + + $ = cheerio.load(body) + companyName = $('.company') + companyNameText = companyName.text() + + $('.company').filter -> + data = $(this) + dataText = data.text() + return + diff --git a/snippets/Node-Cheerio-Simple-Scraping.js b/snippets/Node-Cheerio-Simple-Scraping.js new file mode 100644 index 0000000..83c2a2d --- /dev/null +++ b/snippets/Node-Cheerio-Simple-Scraping.js @@ -0,0 +1,20 @@ +// Generated by CoffeeScript 1.10.0 +var cheerio, request, url; + +request = require('request'); + +cheerio = require('cheerio'); + +url = 'http://www.company.com/jobs/'; + +request(url, function(err, resp, body) { + var $, companyName, companyNameText; + $ = cheerio.load(body); + companyName = $('.company'); + companyNameText = companyName.text(); + return $('.company').filter(function() { + var data, dataText; + data = $(this); + dataText = data.text(); + }); +}); From 1cf23813302f941b3a5097ccbcf4a227fba6ad17 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 17:15:47 +0100 Subject: [PATCH 062/227] Adding custom Event using jQuery --- snippets/jQuery-Events-PubSub.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 snippets/jQuery-Events-PubSub.js diff --git a/snippets/jQuery-Events-PubSub.js b/snippets/jQuery-Events-PubSub.js new file mode 100644 index 0000000..97da1c0 --- /dev/null +++ b/snippets/jQuery-Events-PubSub.js @@ -0,0 +1,8 @@ +$(document).on( 'MyEvent.ready', function( e ) { + + alert("Ready!"); + +}); + +// $(document).trigger( 'MyEvent.ready' [, callback] ); + From a5c8117cedfcb1c0f73e51d46886bcae80d5ada0 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 11 Jan 2016 17:16:45 +0100 Subject: [PATCH 063/227] Adding snippet to test CORS server settings --- snippets/JavaScript-Test-CORS-Server.js | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 snippets/JavaScript-Test-CORS-Server.js diff --git a/snippets/JavaScript-Test-CORS-Server.js b/snippets/JavaScript-Test-CORS-Server.js new file mode 100644 index 0000000..d044226 --- /dev/null +++ b/snippets/JavaScript-Test-CORS-Server.js @@ -0,0 +1,28 @@ +function testCORS(url){ + + url = url || "https://some.url"; + + var createCORSRequest = function(method, url) { + var xhr = new XMLHttpRequest(); + if ("withCredentials" in xhr) { + // Most browsers. + xhr.open(method, url, true); + } else if (typeof XDomainRequest != "undefined") { + // IE8 & IE9 + xhr = new XDomainRequest(); + xhr.open(method, url); + } else { + // CORS not supported. + xhr = null; + } + return xhr; + }; + + var method = 'GET'; + var xhr = createCORSRequest( method, url ); + xhr.onload = function() { console.log( true, "yes" ); }; + xhr.onerror = function() { console.log( false, "no" ); }; + xhr.send(); +} + +testCORS(); \ No newline at end of file From 1a99cea355c389494ab0c08db9661b63955506f0 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 13 Jan 2016 01:11:26 +0100 Subject: [PATCH 064/227] Adding simple PubNub chat code --- snippets/JavaScript-PubNub-Simple-Chat.html | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 snippets/JavaScript-PubNub-Simple-Chat.html diff --git a/snippets/JavaScript-PubNub-Simple-Chat.html b/snippets/JavaScript-PubNub-Simple-Chat.html new file mode 100644 index 0000000..923b9b9 --- /dev/null +++ b/snippets/JavaScript-PubNub-Simple-Chat.html @@ -0,0 +1,46 @@ + + + + + PubNub + + + + + + + +

+ + + + \ No newline at end of file From dd902112c24bd8bb62b8e6421b8b781564f58c95 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 13 Mar 2016 15:17:51 +0100 Subject: [PATCH 065/227] Adding CoffeeScript Cookbook free online book link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 927ac2d..b1ebed6 100755 --- a/README.md +++ b/README.md @@ -495,6 +495,7 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c > BOOKS + * [**CoffeeScript Cookbook**](https://coffeescript-cookbook.github.io/) * [**CoffeeScript Ristretto**](https://leanpub.com/coffeescript-ristretto/read) * [**Smooth CoffeeScript**](http://autotelicum.github.io/Smooth-CoffeeScript/) * [**The Little Book on CoffeeScript**](http://arcturo.github.io/library/coffeescript/) From 4b80504a790758eb1c35746f5f36fd6299076880 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 19 Apr 2018 18:16:10 +0300 Subject: [PATCH 066/227] Adding slides for Web Oversimplified --- WebOversimplified/img/background-scarlet.jpg | Bin 0 -> 56930 bytes WebOversimplified/img/laptop-apache.png | Bin 0 -> 85176 bytes WebOversimplified/img/laptop-browser.png | Bin 0 -> 173659 bytes WebOversimplified/img/laptop.png | Bin 0 -> 4488 bytes WebOversimplified/img/smartphone.png | Bin 0 -> 4934 bytes WebOversimplified/index.html | 362 ++ .../libs/revealjs/css/print/paper.css | 203 + .../libs/revealjs/css/print/pdf.css | 178 + .../libs/revealjs/css/reveal.css | 1555 +++++ .../libs/revealjs/css/reveal.scss | 1717 ++++++ .../libs/revealjs/css/theme/README.md | 21 + .../libs/revealjs/css/theme/beige.css | 268 + .../libs/revealjs/css/theme/black-sha.css | 298 + .../libs/revealjs/css/theme/black.css | 264 + .../libs/revealjs/css/theme/blood.css | 287 + .../libs/revealjs/css/theme/league.css | 270 + .../libs/revealjs/css/theme/moon.css | 268 + .../libs/revealjs/css/theme/night.css | 262 + .../libs/revealjs/css/theme/serif.css | 264 + .../libs/revealjs/css/theme/simple.css | 267 + .../libs/revealjs/css/theme/sky.css | 271 + .../libs/revealjs/css/theme/solarized.css | 268 + .../libs/revealjs/css/theme/source/beige.scss | 39 + .../libs/revealjs/css/theme/source/black.scss | 49 + .../libs/revealjs/css/theme/source/blood.scss | 78 + .../revealjs/css/theme/source/league.scss | 34 + .../libs/revealjs/css/theme/source/moon.scss | 57 + .../libs/revealjs/css/theme/source/night.scss | 34 + .../libs/revealjs/css/theme/source/serif.scss | 35 + .../revealjs/css/theme/source/simple.scss | 43 + .../libs/revealjs/css/theme/source/sky.scss | 46 + .../revealjs/css/theme/source/solarized.scss | 63 + .../libs/revealjs/css/theme/source/white.scss | 49 + .../revealjs/css/theme/template/mixins.scss | 29 + .../revealjs/css/theme/template/settings.scss | 43 + .../revealjs/css/theme/template/theme.scss | 316 + .../libs/revealjs/css/theme/white.css | 264 + WebOversimplified/libs/revealjs/js/reveal.js | 5241 +++++++++++++++++ .../revealjs/lib/css/atom-one-dark-sha.css | 96 + .../revealjs/lib/css/monokai-sublime-sha.css | 85 + .../libs/revealjs/lib/css/zenburn.css | 80 + .../revealjs/lib/font/league-gothic/LICENSE | 2 + .../lib/font/league-gothic/league-gothic.css | 10 + .../lib/font/league-gothic/league-gothic.eot | Bin 0 -> 25696 bytes .../lib/font/league-gothic/league-gothic.ttf | Bin 0 -> 64256 bytes .../lib/font/league-gothic/league-gothic.woff | Bin 0 -> 30764 bytes .../revealjs/lib/font/source-sans-pro/LICENSE | 45 + .../source-sans-pro-italic.eot | Bin 0 -> 75720 bytes .../source-sans-pro-italic.ttf | Bin 0 -> 238084 bytes .../source-sans-pro-italic.woff | Bin 0 -> 98556 bytes .../source-sans-pro-regular.eot | Bin 0 -> 88070 bytes .../source-sans-pro-regular.ttf | Bin 0 -> 288008 bytes .../source-sans-pro-regular.woff | Bin 0 -> 114324 bytes .../source-sans-pro-semibold.eot | Bin 0 -> 89897 bytes .../source-sans-pro-semibold.ttf | Bin 0 -> 284640 bytes .../source-sans-pro-semibold.woff | Bin 0 -> 115648 bytes .../source-sans-pro-semibolditalic.eot | Bin 0 -> 75706 bytes .../source-sans-pro-semibolditalic.ttf | Bin 0 -> 240944 bytes .../source-sans-pro-semibolditalic.woff | Bin 0 -> 98816 bytes .../font/source-sans-pro/source-sans-pro.css | 39 + .../libs/revealjs/lib/js/classList.js | 2 + .../libs/revealjs/lib/js/head.min.js | 6 + .../libs/revealjs/lib/js/html5shiv.js | 7 + .../revealjs/plugin/highlight/highlight.js | 77 + .../revealjs/plugin/markdown/example.html | 136 + .../libs/revealjs/plugin/markdown/example.md | 36 + .../libs/revealjs/plugin/markdown/markdown.js | 412 ++ .../libs/revealjs/plugin/markdown/marked.js | 6 + .../libs/revealjs/plugin/math/math.js | 67 + .../libs/revealjs/plugin/multiplex/client.js | 13 + .../libs/revealjs/plugin/multiplex/index.js | 64 + .../libs/revealjs/plugin/multiplex/master.js | 34 + .../revealjs/plugin/multiplex/package.json | 19 + .../revealjs/plugin/notes-server/client.js | 65 + .../revealjs/plugin/notes-server/index.js | 69 + .../revealjs/plugin/notes-server/notes.html | 585 ++ .../libs/revealjs/plugin/notes/notes.html | 759 +++ .../libs/revealjs/plugin/notes/notes.js | 155 + .../revealjs/plugin/print-pdf/print-pdf.js | 69 + .../libs/revealjs/plugin/search/search.js | 206 + .../libs/revealjs/plugin/zoom-js/zoom.js | 272 + 81 files changed, 16859 insertions(+) create mode 100644 WebOversimplified/img/background-scarlet.jpg create mode 100644 WebOversimplified/img/laptop-apache.png create mode 100644 WebOversimplified/img/laptop-browser.png create mode 100644 WebOversimplified/img/laptop.png create mode 100644 WebOversimplified/img/smartphone.png create mode 100644 WebOversimplified/index.html create mode 100644 WebOversimplified/libs/revealjs/css/print/paper.css create mode 100644 WebOversimplified/libs/revealjs/css/print/pdf.css create mode 100644 WebOversimplified/libs/revealjs/css/reveal.css create mode 100644 WebOversimplified/libs/revealjs/css/reveal.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/README.md create mode 100644 WebOversimplified/libs/revealjs/css/theme/beige.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/black-sha.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/black.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/blood.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/league.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/moon.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/night.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/serif.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/simple.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/sky.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/solarized.css create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/beige.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/black.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/blood.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/league.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/moon.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/night.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/serif.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/simple.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/sky.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/solarized.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/source/white.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/template/mixins.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/template/settings.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/template/theme.scss create mode 100644 WebOversimplified/libs/revealjs/css/theme/white.css create mode 100644 WebOversimplified/libs/revealjs/js/reveal.js create mode 100644 WebOversimplified/libs/revealjs/lib/css/atom-one-dark-sha.css create mode 100644 WebOversimplified/libs/revealjs/lib/css/monokai-sublime-sha.css create mode 100644 WebOversimplified/libs/revealjs/lib/css/zenburn.css create mode 100644 WebOversimplified/libs/revealjs/lib/font/league-gothic/LICENSE create mode 100644 WebOversimplified/libs/revealjs/lib/font/league-gothic/league-gothic.css create mode 100755 WebOversimplified/libs/revealjs/lib/font/league-gothic/league-gothic.eot create mode 100755 WebOversimplified/libs/revealjs/lib/font/league-gothic/league-gothic.ttf create mode 100755 WebOversimplified/libs/revealjs/lib/font/league-gothic/league-gothic.woff create mode 100644 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/LICENSE create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-italic.eot create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-italic.ttf create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-italic.woff create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-regular.eot create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-regular.ttf create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-regular.woff create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibold.eot create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibold.ttf create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibold.woff create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf create mode 100755 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff create mode 100644 WebOversimplified/libs/revealjs/lib/font/source-sans-pro/source-sans-pro.css create mode 100644 WebOversimplified/libs/revealjs/lib/js/classList.js create mode 100644 WebOversimplified/libs/revealjs/lib/js/head.min.js create mode 100644 WebOversimplified/libs/revealjs/lib/js/html5shiv.js create mode 100644 WebOversimplified/libs/revealjs/plugin/highlight/highlight.js create mode 100644 WebOversimplified/libs/revealjs/plugin/markdown/example.html create mode 100644 WebOversimplified/libs/revealjs/plugin/markdown/example.md create mode 100755 WebOversimplified/libs/revealjs/plugin/markdown/markdown.js create mode 100644 WebOversimplified/libs/revealjs/plugin/markdown/marked.js create mode 100755 WebOversimplified/libs/revealjs/plugin/math/math.js create mode 100644 WebOversimplified/libs/revealjs/plugin/multiplex/client.js create mode 100644 WebOversimplified/libs/revealjs/plugin/multiplex/index.js create mode 100644 WebOversimplified/libs/revealjs/plugin/multiplex/master.js create mode 100644 WebOversimplified/libs/revealjs/plugin/multiplex/package.json create mode 100644 WebOversimplified/libs/revealjs/plugin/notes-server/client.js create mode 100644 WebOversimplified/libs/revealjs/plugin/notes-server/index.js create mode 100644 WebOversimplified/libs/revealjs/plugin/notes-server/notes.html create mode 100644 WebOversimplified/libs/revealjs/plugin/notes/notes.html create mode 100644 WebOversimplified/libs/revealjs/plugin/notes/notes.js create mode 100644 WebOversimplified/libs/revealjs/plugin/print-pdf/print-pdf.js create mode 100644 WebOversimplified/libs/revealjs/plugin/search/search.js create mode 100644 WebOversimplified/libs/revealjs/plugin/zoom-js/zoom.js diff --git a/WebOversimplified/img/background-scarlet.jpg b/WebOversimplified/img/background-scarlet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7087450e3bc9b9edc3fdd0215fbe4d6db8b7a797 GIT binary patch literal 56930 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�rp~N$h~UzJ#^`6s|6_e;Db9@F~rh8!7?wp28!_ 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 literal 0 HcmV?d00001 diff --git a/WebOversimplified/img/laptop-browser.png b/WebOversimplified/img/laptop-browser.png new file mode 100644 index 0000000000000000000000000000000000000000..6dd61d4abff61ca93039a610c7744f8da67037d5 GIT binary patch literal 173659 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

ll}0nv-eN-P_9m1XzX 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 literal 0 HcmV?d00001 diff --git a/WebOversimplified/img/laptop.png b/WebOversimplified/img/laptop.png new file mode 100644 index 0000000000000000000000000000000000000000..cb93b3f11f16180c10d3c652ff600fb3c06b1f16 GIT binary patch literal 4488 zcmds42~<;O7XA~K1Ox$76{$*a2~@y@#UjF(M<8rN5C}yS*;GKQEW;+K$P+S(SQLs2 zQV@!aP#DFX3I|ZIfa8E=5l#^#TBZf0h!~21A@jc%!xEOxIpdtuPEJl<-hKD~mV592 zUUI_8(MDNOLlFR=Y-elb0)Q<1DhtRm@L*f@^f~|p)XBk>EfR^&{_S#cd1dSuNeSo9 zXBS*4tE!pj4by&~@1o+8|3p~swHr4}%THg-nn*UX_0(rN>mytP>+N*bXGS(22JCHy z*4x)xxEk5)&_`V87Ou`a1C8yx^fo)eReljMus()19_taVKGVg>b_c_9tFf)89@Cj& zwG|dKDI;^e1(#vv&aiZ&Te=xoxf@!07~6U3ZFV+byL;@9h~ypj3W^Fme3Z^|JFOq> z4_o3E?&8P=?$!BUw&@;4^;%k=#I4hpu`BK$AU=)$vg#BgSUo2rf~qunGbym4S7@)` zTVUfw86J7WuCchYAWyl8qxdF%Ty1UL<~9D7W7_{}_DgJW3@_Zjx|II-=Y0tPsQB7h zS-3`d_1z;VL%vGtSl*o)k}ft5|7S1src4bDpbS+Yx73!d*=Tjk&R#o2cG&wnt-te_ z_fHtNj>@fyc(f;ZFfr15mse!JZeE=Z$QjUke&9R#HM zklBkcqJOM6Z7e?7kT#$Yvu&hAz!6lq?aREQkhQ&(>#;bdeOJsL7U9^CO+$^qgrigK z?`?edN+2ciM%mi*e>%4EUW6a8diC)y=}x&FDcvgq#%LdXE^-iTA#o^)Ps~-zu0$N( z$-3`&$|J`lqlKfhL+C0f81bVd7Ms%#viCLcz)AgvKR3~=(w3Bk__7ifG^^QvA=aH(^O-V*xM5vNcn~W$EG0acm3t^f;KRx#rBu^+8YlfZ-< z^g8I|m;#Wc0#y_sfCvcW|N6v7o{Ru0yxGBg71OOshms}6aK;PxE3?Xc_$ ztxR}QUXyDG2={K`*@wq38^M#GHsIOM@a%J~xmc_f$2N)Ve`$SGCn1M#ZL0gcPVTvl zMg~8C+i4sX-qYp8>?mns69LHIUE_xze&)I?0id>PizfioRHh#Rpbq0_}rb4J1XK6m%p^rP5LN@EF z?efz+#8yZc#l|MJwG~ysCwp=etPwSjIhTxITD9i_cqfFN5c`-!m97?!>=+9%HhSh| zrOI2HT;J9^ePuq57GVy{u~xpHgz0sZYx%K%>0}$Hrsf7Ce%_D zv=`e)jFHs<-0M0GEu}J{UWak=)FApj&WyYGt!;J~+dH_$sM}ay=0=@C)loDTKeMM{ zEo`({W^2|QX}<*+GORmO&%ls27TkhJN>V-!_h;HWHA$UHy`dpm?6e?qWJQ){WPjH& z3u!$f=wBp&a4-IbXEtG1bxhr)q#D@7I3s>*%sUCl3%2H%99*}E&JF&HhYJzh$Ohi zt$|LcAYN~uNYQOTlZSxdkL8JE83OMps6>D-75+{~)X7vOS=Ik>IDS{2SCBfSRs5;T zFHp&^M6@vUv6jmjP5ybbEif`9oy0BD$6JC-$r#bXaE2J literal 0 HcmV?d00001 diff --git a/WebOversimplified/img/smartphone.png b/WebOversimplified/img/smartphone.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca61341270fa3b231b7ff5e0e3eb8428a58011a GIT binary patch literal 4934 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 Jd$^4k_CKGa#+?8F literal 0 HcmV?d00001 diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html new file mode 100644 index 0000000..515eb78 --- /dev/null +++ b/WebOversimplified/index.html @@ -0,0 +1,362 @@ + + + + + + + JavaScript Module 1 | Week 02 Part II | Social Hackers Academy + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

Node.JS 101

+

101 Course | Week 01

+

Complementary Material

+

+ By Kostas Minaidis +

+
+ + +
+

The Web

+

(Oversimplified)

+
    +
  • Web Servers & HTTP
  • +
  • IP Addresses & DNS
  • +
  • Port Addresses
  • +
+
+ + +
+ +
+

Web Servers & HTTP

+

Oversimplified

+
+ +
+ +

What is a Web Server?

+
+ + A computer, just like yours*,
holding files and running programs.
+
+ A server is always connected to the Internet. +
+ * + well, somewhat more powerful than your laptop... +
+ +
+ +

Let's talk!

+
+
+ + + Server +
+ + Hello! + +
+
+ + + Your Computer +
+ + Hi! + +
+
+ + Your computer can communicate
with Internet servers, through various protocols*
+ +
+ +
+ + +
+ +

Internet Protocols!

+

(Don't be afraid of the term.)

+ You can think of protocols as a way for computers to speak
with other computers on a network such as the Internet.
+

+ ...much like people talk to each other in different languages
(english, greek, arabic, etc.).
+

+ Your Web browser uses the HTTP protocol to talk to Web servers. + +
+ + +
+ +
+

Let's talk HTTP

+

Web Browsers & Web Servers

+
+ +
+ +
+
+ + + Web Server* +
+ *A Server, holding and serving web pages.
( It can 'talk' HTTP )
+

+ +   + +
+
+ + + Your Web Browser* +
+ *Software that fetches and displays web pages.
( It can 'talk' HTTP too )
+

+
+
+ + + <--[ GET /index.html ]-- + +
+ + The Web Browser, requests the index.html file
from the Web Server using a simple HTTP command: GET +
+

+ + --[ index.html ]--> + +
+ + The Web Server, responds by sending the index.html
file to the Web Browser, which in turn renders
and displays the web page to the user. +
+ +
+
+ + +
+ +
+

Let me call you!

+

IP Addresses & Domain Name Servers

+
+ +
+ +
+
+ + + Web Server* +
+ *A Server, holding and serving web pages.
( It can 'talk' HTTP )
+

+ +   + +
+
+ + + Your Web Browser* +
+ *Software that fetches and displays web pages.
( It can 'talk' HTTP too )
+

+
+
+ + + <--[ GET /index.html ]-- + +
+ + The Web Browser, requests the index.html file
from the Web Server using a simple HTTP command: GET +
+

+ + --[ index.html ]--> + +
+ + The Web Server, responds by sending the index.html
file to the Web Browser, which in turn renders
and displays the web page to the user. +
+ +
+
+ + +
+ +
+

Network Ports

+

...

+
+ +
+ +
+
+ + + Web Server* +
+ *A Server, holding and serving web pages.
( It can 'talk' HTTP )
+

+ +   + +
+
+ + + Your Web Browser* +
+ *Software that fetches and displays web pages.
( It can 'talk' HTTP too )
+

+
+
+ + + <--[ GET /index.html ]-- + +
+ + The Web Browser, requests the index.html file
from the Web Server using a simple HTTP command: GET +
+

+ + --[ index.html ]--> + +
+ + The Web Server, responds by sending the index.html
file to the Web Browser, which in turn renders
and displays the web page to the user. +
+ +
+
+ + +
+ +

That's all Folks!

+
+ +
+ +
+ + + + + + + + diff --git a/WebOversimplified/libs/revealjs/css/print/paper.css b/WebOversimplified/libs/revealjs/css/print/paper.css new file mode 100644 index 0000000..27d19dd --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/print/paper.css @@ -0,0 +1,203 @@ +/* Default Print Stylesheet Template + by Rob Glazebrook of CSSnewbie.com + Last Updated: June 4, 2008 + + Feel free (nay, compelled) to edit, append, and + manipulate this file as you see fit. */ + + +@media print { + + /* SECTION 1: Set default width, margin, float, and + background. This prevents elements from extending + beyond the edge of the printed page, and prevents + unnecessary background images from printing */ + html { + background: #fff; + width: auto; + height: auto; + overflow: visible; + } + body { + background: #fff; + font-size: 20pt; + width: auto; + height: auto; + border: 0; + margin: 0 5%; + padding: 0; + overflow: visible; + float: none !important; + } + + /* SECTION 2: Remove any elements not needed in print. + This would include navigation, ads, sidebars, etc. */ + .nestedarrow, + .controls, + .fork-reveal, + .share-reveal, + .state-background, + .reveal .progress, + .reveal .backgrounds, + .reveal .slide-number { + display: none !important; + } + + /* SECTION 3: Set body font face, size, and color. + Consider using a serif font for readability. */ + body, p, td, li, div { + font-size: 20pt!important; + font-family: Georgia, "Times New Roman", Times, serif !important; + color: #000; + } + + /* SECTION 4: Set heading font face, sizes, and color. + Differentiate your headings from your body text. + Perhaps use a large sans-serif for distinction. */ + h1,h2,h3,h4,h5,h6 { + color: #000!important; + height: auto; + line-height: normal; + font-family: Georgia, "Times New Roman", Times, serif !important; + text-shadow: 0 0 0 #000 !important; + text-align: left; + letter-spacing: normal; + } + /* Need to reduce the size of the fonts for printing */ + h1 { font-size: 28pt !important; } + h2 { font-size: 24pt !important; } + h3 { font-size: 22pt !important; } + h4 { font-size: 22pt !important; font-variant: small-caps; } + h5 { font-size: 21pt !important; } + h6 { font-size: 20pt !important; font-style: italic; } + + /* SECTION 5: Make hyperlinks more usable. + Ensure links are underlined, and consider appending + the URL to the end of the link for usability. */ + a:link, + a:visited { + color: #000 !important; + font-weight: bold; + text-decoration: underline; + } + /* + .reveal a:link:after, + .reveal a:visited:after { + content: " (" attr(href) ") "; + color: #222 !important; + font-size: 90%; + } + */ + + + /* SECTION 6: more reveal.js specific additions by @skypanther */ + ul, ol, div, p { + visibility: visible; + position: static; + width: auto; + height: auto; + display: block; + overflow: visible; + margin: 0; + text-align: left !important; + } + .reveal pre, + .reveal table { + margin-left: 0; + margin-right: 0; + } + .reveal pre code { + padding: 20px; + border: 1px solid #ddd; + } + .reveal blockquote { + margin: 20px 0; + } + .reveal .slides { + position: static !important; + width: auto !important; + height: auto !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 0 !important; + zoom: 1 !important; + + overflow: visible !important; + display: block !important; + + text-align: left !important; + -webkit-perspective: none; + -moz-perspective: none; + -ms-perspective: none; + perspective: none; + + -webkit-perspective-origin: 50% 50%; + -moz-perspective-origin: 50% 50%; + -ms-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; + } + .reveal .slides section { + visibility: visible !important; + position: static !important; + width: auto !important; + height: auto !important; + display: block !important; + overflow: visible !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 60px 20px !important; + z-index: auto !important; + + opacity: 1 !important; + + page-break-after: always !important; + + -webkit-transform-style: flat !important; + -moz-transform-style: flat !important; + -ms-transform-style: flat !important; + transform-style: flat !important; + + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + transform: none !important; + + -webkit-transition: none !important; + -moz-transition: none !important; + -ms-transition: none !important; + transition: none !important; + } + .reveal .slides section.stack { + padding: 0 !important; + } + .reveal section:last-of-type { + page-break-after: avoid !important; + } + .reveal section .fragment { + opacity: 1 !important; + visibility: visible !important; + + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + transform: none !important; + } + .reveal section img { + display: block; + margin: 15px 0px; + background: rgba(255,255,255,1); + border: 1px solid #666; + box-shadow: none; + } + + .reveal section small { + font-size: 0.8em; + } + +} diff --git a/WebOversimplified/libs/revealjs/css/print/pdf.css b/WebOversimplified/libs/revealjs/css/print/pdf.css new file mode 100644 index 0000000..aa1f7fa --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/print/pdf.css @@ -0,0 +1,178 @@ +/** + * This stylesheet is used to print reveal.js + * presentations to PDF. + * + * https://github.com/hakimel/reveal.js#pdf-export + */ + +* { + -webkit-print-color-adjust: exact; +} + +body { + margin: 0 auto !important; + border: 0; + padding: 0; + float: none !important; + overflow: visible; +} + +html { + width: 100%; + height: 100%; + overflow: visible; +} + +/* Remove any elements not needed in print. */ +.nestedarrow, +.reveal .controls, +.reveal .progress, +.reveal .playback, +.reveal.overview, +.fork-reveal, +.share-reveal, +.state-background { + display: none !important; +} + +h1, h2, h3, h4, h5, h6 { + text-shadow: 0 0 0 #000 !important; +} + +.reveal pre code { + overflow: hidden !important; + font-family: Courier, 'Courier New', monospace !important; +} + +ul, ol, div, p { + visibility: visible; + position: static; + width: auto; + height: auto; + display: block; + overflow: visible; + margin: auto; +} +.reveal { + width: auto !important; + height: auto !important; + overflow: hidden !important; +} +.reveal .slides { + position: static; + width: 100% !important; + height: auto !important; + zoom: 1 !important; + + left: auto; + top: auto; + margin: 0 !important; + padding: 0 !important; + + overflow: visible; + display: block; + + -webkit-perspective: none; + -moz-perspective: none; + -ms-perspective: none; + perspective: none; + + -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ + -moz-perspective-origin: 50% 50%; + -ms-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; +} + +.reveal .slides .pdf-page { + position: relative; + overflow: hidden; + z-index: 1; + + page-break-after: always; +} + +.reveal .slides section { + visibility: visible !important; + display: block !important; + position: absolute !important; + + margin: 0 !important; + padding: 0 !important; + box-sizing: border-box !important; + min-height: 1px; + + opacity: 1 !important; + + -webkit-transform-style: flat !important; + -moz-transform-style: flat !important; + -ms-transform-style: flat !important; + transform-style: flat !important; + + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + transform: none !important; +} + +.reveal section.stack { + position: relative !important; + margin: 0 !important; + padding: 0 !important; + page-break-after: avoid !important; + height: auto !important; + min-height: auto !important; +} + +.reveal img { + box-shadow: none; +} + +.reveal .roll { + overflow: visible; + line-height: 1em; +} + +/* Slide backgrounds are placed inside of their slide when exporting to PDF */ +.reveal .slide-background { + display: block !important; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: auto !important; +} + +/* Display slide speaker notes when 'showNotes' is enabled */ +.reveal.show-notes { + max-width: none; + max-height: none; +} +.reveal .speaker-notes-pdf { + display: block; + width: 100%; + height: auto; + max-height: none; + top: auto; + right: auto; + bottom: auto; + left: auto; + z-index: 100; +} + +/* Layout option which makes notes appear on a separate page */ +.reveal .speaker-notes-pdf[data-layout="separate-page"] { + position: relative; + color: inherit; + background-color: transparent; + padding: 20px; + page-break-after: always; + border: 0; +} + +/* Display slide numbers when 'slideNumber' is enabled */ +.reveal .slide-number-pdf { + display: block; + position: absolute; + font-size: 14px; +} diff --git a/WebOversimplified/libs/revealjs/css/reveal.css b/WebOversimplified/libs/revealjs/css/reveal.css new file mode 100644 index 0000000..3392753 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/reveal.css @@ -0,0 +1,1555 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2017 Hakim El Hattab, http://hakim.se + */ +/********************************************* + * RESET STYLES + *********************************************/ +html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, +.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, +.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, +.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, +.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, +.reveal b, .reveal u, .reveal center, +.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, +.reveal fieldset, .reveal form, .reveal label, .reveal legend, +.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, +.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, +.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, +.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, +.reveal time, .reveal mark, .reveal audio, .reveal video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; } + +.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, +.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { + display: block; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +html, +body { + width: 100%; + height: 100%; + overflow: hidden; } + +body { + position: relative; + line-height: 1; + background-color: #fff; + color: #000; } + +/********************************************* + * VIEW FRAGMENTS + *********************************************/ +.reveal .slides section .fragment { + opacity: 0; + visibility: hidden; + transition: all .2s ease; } + .reveal .slides section .fragment.visible { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.grow { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.grow.visible { + -webkit-transform: scale(1.3); + transform: scale(1.3); } + +.reveal .slides section .fragment.shrink { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.shrink.visible { + -webkit-transform: scale(0.7); + transform: scale(0.7); } + +.reveal .slides section .fragment.zoom-in { + -webkit-transform: scale(0.1); + transform: scale(0.1); } + .reveal .slides section .fragment.zoom-in.visible { + -webkit-transform: none; + transform: none; } + +.reveal .slides section .fragment.fade-out { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.fade-out.visible { + opacity: 0; + visibility: hidden; } + +.reveal .slides section .fragment.semi-fade-out { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.semi-fade-out.visible { + opacity: 0.5; + visibility: inherit; } + +.reveal .slides section .fragment.strike { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.strike.visible { + text-decoration: line-through; } + +.reveal .slides section .fragment.fade-up { + -webkit-transform: translate(0, 20%); + transform: translate(0, 20%); } + .reveal .slides section .fragment.fade-up.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-down { + -webkit-transform: translate(0, -20%); + transform: translate(0, -20%); } + .reveal .slides section .fragment.fade-down.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-right { + -webkit-transform: translate(-20%, 0); + transform: translate(-20%, 0); } + .reveal .slides section .fragment.fade-right.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-left { + -webkit-transform: translate(20%, 0); + transform: translate(20%, 0); } + .reveal .slides section .fragment.fade-left.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.current-visible { + opacity: 0; + visibility: hidden; } + .reveal .slides section .fragment.current-visible.current-fragment { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.highlight-red, +.reveal .slides section .fragment.highlight-current-red, +.reveal .slides section .fragment.highlight-green, +.reveal .slides section .fragment.highlight-current-green, +.reveal .slides section .fragment.highlight-blue, +.reveal .slides section .fragment.highlight-current-blue { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.highlight-red.visible { + color: #ff2c2d; } + +.reveal .slides section .fragment.highlight-green.visible { + color: #17ff2e; } + +.reveal .slides section .fragment.highlight-blue.visible { + color: #1b91ff; } + +.reveal .slides section .fragment.highlight-current-red.current-fragment { + color: #ff2c2d; } + +.reveal .slides section .fragment.highlight-current-green.current-fragment { + color: #17ff2e; } + +.reveal .slides section .fragment.highlight-current-blue.current-fragment { + color: #1b91ff; } + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ +/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ +.reveal:after { + content: ''; + font-style: italic; } + +.reveal iframe { + z-index: 1; } + +/** Prevents layering issues in certain browser/transition combinations */ +.reveal a { + position: relative; } + +.reveal .stretch { + max-width: none; + max-height: none; } + +.reveal pre.stretch code { + height: 100%; + max-height: 100%; + box-sizing: border-box; } + +/********************************************* + * CONTROLS + *********************************************/ +@-webkit-keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } + +@-webkit-keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +.reveal .controls { + display: none; + position: absolute; + top: auto; + bottom: 12px; + right: 12px; + left: auto; + z-index: 1; + color: #000; + pointer-events: none; + font-size: 10px; } + .reveal .controls button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + -webkit-transform: scale(0.9999); + transform: scale(0.9999); + transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease; + transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease; + z-index: 2; + pointer-events: auto; + font-size: inherit; + visibility: hidden; + opacity: 0; + -webkit-appearance: none; + -webkit-tap-highlight-color: transparent; } + .reveal .controls .controls-arrow:before, + .reveal .controls .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 2.6em; + height: 0.5em; + border-radius: 0.25em; + background-color: currentColor; + transition: all 0.15s ease, background-color 0.8s ease; + -webkit-transform-origin: 0.2em 50%; + transform-origin: 0.2em 50%; + will-change: transform; } + .reveal .controls .controls-arrow { + position: relative; + width: 3.6em; + height: 3.6em; } + .reveal .controls .controls-arrow:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + .reveal .controls .controls-arrow:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + .reveal .controls .controls-arrow:hover:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(40deg); } + .reveal .controls .controls-arrow:hover:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); } + .reveal .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(36deg); } + .reveal .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); } + .reveal .controls .navigate-left { + right: 6.4em; + bottom: 3.2em; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); } + .reveal .controls .navigate-right { + right: 0; + bottom: 3.2em; + -webkit-transform: translateX(10px); + transform: translateX(10px); } + .reveal .controls .navigate-right .controls-arrow { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + .reveal .controls .navigate-right.highlight { + -webkit-animation: bounce-right 2s 50 both ease-out; + animation: bounce-right 2s 50 both ease-out; } + .reveal .controls .navigate-up { + right: 3.2em; + bottom: 6.4em; + -webkit-transform: translateY(-10px); + transform: translateY(-10px); } + .reveal .controls .navigate-up .controls-arrow { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + .reveal .controls .navigate-down { + right: 3.2em; + bottom: 0; + -webkit-transform: translateY(10px); + transform: translateY(10px); } + .reveal .controls .navigate-down .controls-arrow { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); } + .reveal .controls .navigate-down.highlight { + -webkit-animation: bounce-down 2s 50 both ease-out; + animation: bounce-down 2s 50 both ease-out; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover { + opacity: 1; } + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; } + .reveal .controls .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + -webkit-transform: none; + transform: none; } + .reveal .controls .enabled.fragmented { + opacity: 0.5; } + .reveal .controls .enabled:hover, + .reveal .controls .enabled.fragmented:hover { + opacity: 1; } + +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: 1.4em; + right: 5.5em; } + +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: 1.4em; + right: 0.5em; } + +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: 1.4em; + bottom: 5em; } + +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: 1.4em; + bottom: 0.5em; } + +.reveal.has-dark-background .controls { + color: #fff; } + +.reveal.has-light-background .controls { + color: #000; } + +.reveal.no-hover .controls .controls-arrow:hover:before, +.reveal.no-hover .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + +.reveal.no-hover .controls .controls-arrow:hover:after, +.reveal.no-hover .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + +@media screen and (min-width: 500px) { + .reveal .controls[data-controls-layout="edges"] { + top: 0; + right: 0; + bottom: 0; + left: 0; } + .reveal .controls[data-controls-layout="edges"] .navigate-left, + .reveal .controls[data-controls-layout="edges"] .navigate-right, + .reveal .controls[data-controls-layout="edges"] .navigate-up, + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: auto; + right: auto; } + .reveal .controls[data-controls-layout="edges"] .navigate-left { + top: 50%; + left: 8px; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-right { + top: 50%; + right: 8px; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-up { + top: 8px; + left: 50%; + margin-left: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: 8px; + left: 50%; + margin-left: -1.8em; } } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + position: absolute; + display: none; + height: 3px; + width: 100%; + bottom: 0; + left: 0; + z-index: 10; + background-color: rgba(0, 0, 0, 0.2); + color: #fff; } + +.reveal .progress:after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + top: -10px; } + +.reveal .progress span { + display: block; + height: 100%; + width: 0px; + background-color: currentColor; + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * SLIDE NUMBER + *********************************************/ +.reveal .slide-number { + position: fixed; + display: block; + right: 8px; + bottom: 8px; + z-index: 31; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba(0, 0, 0, 0.4); + padding: 5px; } + +.reveal .slide-number-delimiter { + margin: 0 3px; } + +/********************************************* + * SLIDES + *********************************************/ +.reveal { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + -ms-touch-action: none; + touch-action: none; } + +@media only screen and (orientation: landscape) { + .reveal.ua-iphone { + position: fixed; } } + +.reveal .slides { + position: absolute; + width: 100%; + height: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + pointer-events: none; + overflow: visible; + z-index: 1; + text-align: center; + -webkit-perspective: 600px; + perspective: 600px; + -webkit-perspective-origin: 50% 40%; + perspective-origin: 50% 40%; } + +.reveal .slides > section { + -ms-perspective: 600px; } + +.reveal .slides > section, +.reveal .slides > section > section { + display: none; + position: absolute; + width: 100%; + padding: 20px 0px; + pointer-events: auto; + z-index: 10; + -webkit-transform-style: flat; + transform-style: flat; + transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] .slides section { + transition-duration: 400ms; } + +.reveal[data-transition-speed="slow"] .slides section { + transition-duration: 1200ms; } + +/* Slide-specific transition speed overrides */ +.reveal .slides section[data-transition-speed="fast"] { + transition-duration: 400ms; } + +.reveal .slides section[data-transition-speed="slow"] { + transition-duration: 1200ms; } + +.reveal .slides > section.stack { + padding-top: 0; + padding-bottom: 0; } + +.reveal .slides > section.present, +.reveal .slides > section > section.present { + display: block; + z-index: 11; + opacity: 1; } + +.reveal .slides > section:empty, +.reveal .slides > section > section:empty, +.reveal .slides > section[data-background-interactive], +.reveal .slides > section > section[data-background-interactive] { + pointer-events: none; } + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { + min-height: 0 !important; } + +/* Don't allow interaction with invisible slides */ +.reveal .slides > section.future, +.reveal .slides > section > section.future, +.reveal .slides > section.past, +.reveal .slides > section > section.past { + pointer-events: none; } + +.reveal.overview .slides > section, +.reveal.overview .slides > section > section { + pointer-events: auto; } + +.reveal .slides > section.past, +.reveal .slides > section.future, +.reveal .slides > section > section.past, +.reveal .slides > section > section.future { + opacity: 0; } + +/********************************************* + * Mixins for readability of transitions + *********************************************/ +/********************************************* + * SLIDE TRANSITION + * Aliased 'linear' for backwards compatibility + *********************************************/ +.reveal.slide section { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .slides > section[data-transition=slide].past, +.reveal .slides > section[data-transition~=slide-out].past, +.reveal.slide .slides > section:not([data-transition]).past { + -webkit-transform: translate(-150%, 0); + transform: translate(-150%, 0); } + +.reveal .slides > section[data-transition=slide].future, +.reveal .slides > section[data-transition~=slide-in].future, +.reveal.slide .slides > section:not([data-transition]).future { + -webkit-transform: translate(150%, 0); + transform: translate(150%, 0); } + +.reveal .slides > section > section[data-transition=slide].past, +.reveal .slides > section > section[data-transition~=slide-out].past, +.reveal.slide .slides > section > section:not([data-transition]).past { + -webkit-transform: translate(0, -150%); + transform: translate(0, -150%); } + +.reveal .slides > section > section[data-transition=slide].future, +.reveal .slides > section > section[data-transition~=slide-in].future, +.reveal.slide .slides > section > section:not([data-transition]).future { + -webkit-transform: translate(0, 150%); + transform: translate(0, 150%); } + +.reveal.linear section { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .slides > section[data-transition=linear].past, +.reveal .slides > section[data-transition~=linear-out].past, +.reveal.linear .slides > section:not([data-transition]).past { + -webkit-transform: translate(-150%, 0); + transform: translate(-150%, 0); } + +.reveal .slides > section[data-transition=linear].future, +.reveal .slides > section[data-transition~=linear-in].future, +.reveal.linear .slides > section:not([data-transition]).future { + -webkit-transform: translate(150%, 0); + transform: translate(150%, 0); } + +.reveal .slides > section > section[data-transition=linear].past, +.reveal .slides > section > section[data-transition~=linear-out].past, +.reveal.linear .slides > section > section:not([data-transition]).past { + -webkit-transform: translate(0, -150%); + transform: translate(0, -150%); } + +.reveal .slides > section > section[data-transition=linear].future, +.reveal .slides > section > section[data-transition~=linear-in].future, +.reveal.linear .slides > section > section:not([data-transition]).future { + -webkit-transform: translate(0, 150%); + transform: translate(0, 150%); } + +/********************************************* + * CONVEX TRANSITION + * Aliased 'default' for backwards compatibility + *********************************************/ +.reveal .slides section[data-transition=default].stack, +.reveal.default .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=default].past, +.reveal .slides > section[data-transition~=default-out].past, +.reveal.default .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=default].future, +.reveal .slides > section[data-transition~=default-in].future, +.reveal.default .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=default].past, +.reveal .slides > section > section[data-transition~=default-out].past, +.reveal.default .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } + +.reveal .slides > section > section[data-transition=default].future, +.reveal .slides > section > section[data-transition~=default-in].future, +.reveal.default .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } + +.reveal .slides section[data-transition=convex].stack, +.reveal.convex .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=convex].past, +.reveal .slides > section[data-transition~=convex-out].past, +.reveal.convex .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=convex].future, +.reveal .slides > section[data-transition~=convex-in].future, +.reveal.convex .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=convex].past, +.reveal .slides > section > section[data-transition~=convex-out].past, +.reveal.convex .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } + +.reveal .slides > section > section[data-transition=convex].future, +.reveal .slides > section > section[data-transition~=convex-in].future, +.reveal.convex .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } + +/********************************************* + * CONCAVE TRANSITION + *********************************************/ +.reveal .slides section[data-transition=concave].stack, +.reveal.concave .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=concave].past, +.reveal .slides > section[data-transition~=concave-out].past, +.reveal.concave .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=concave].future, +.reveal .slides > section[data-transition~=concave-in].future, +.reveal.concave .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=concave].past, +.reveal .slides > section > section[data-transition~=concave-out].past, +.reveal.concave .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); + transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); } + +.reveal .slides > section > section[data-transition=concave].future, +.reveal .slides > section > section[data-transition~=concave-in].future, +.reveal.concave .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); + transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); } + +/********************************************* + * ZOOM TRANSITION + *********************************************/ +.reveal .slides section[data-transition=zoom], +.reveal.zoom .slides section:not([data-transition]) { + transition-timing-function: ease; } + +.reveal .slides > section[data-transition=zoom].past, +.reveal .slides > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section:not([data-transition]).past { + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal .slides > section[data-transition=zoom].future, +.reveal .slides > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section:not([data-transition]).future { + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal .slides > section > section[data-transition=zoom].past, +.reveal .slides > section > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section > section:not([data-transition]).past { + -webkit-transform: translate(0, -150%); + transform: translate(0, -150%); } + +.reveal .slides > section > section[data-transition=zoom].future, +.reveal .slides > section > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section > section:not([data-transition]).future { + -webkit-transform: translate(0, 150%); + transform: translate(0, 150%); } + +/********************************************* + * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ +.reveal.cube .slides { + -webkit-perspective: 1300px; + perspective: 1300px; } + +.reveal.cube .slides section { + padding: 30px; + min-height: 700px; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal.center.cube .slides section { + min-height: 0; } + +.reveal.cube .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + -webkit-transform: translateZ(-20px); + transform: translateZ(-20px); } + +.reveal.cube .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2); + -webkit-transform: translateZ(-90px) rotateX(65deg); + transform: translateZ(-90px) rotateX(65deg); } + +.reveal.cube .slides > section.stack { + padding: 0; + background: none; } + +.reveal.cube .slides > section.past { + -webkit-transform-origin: 100% 0%; + transform-origin: 100% 0%; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg); + transform: translate3d(-100%, 0, 0) rotateY(-90deg); } + +.reveal.cube .slides > section.future { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg); + transform: translate3d(100%, 0, 0) rotateY(90deg); } + +.reveal.cube .slides > section > section.past { + -webkit-transform-origin: 0% 100%; + transform-origin: 0% 100%; + -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg); + transform: translate3d(0, -100%, 0) rotateX(90deg); } + +.reveal.cube .slides > section > section.future { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg); + transform: translate3d(0, 100%, 0) rotateX(-90deg); } + +/********************************************* + * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ +.reveal.page .slides { + -webkit-perspective-origin: 0% 50%; + perspective-origin: 0% 50%; + -webkit-perspective: 3000px; + perspective: 3000px; } + +.reveal.page .slides section { + padding: 30px; + min-height: 700px; + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal.page .slides section.past { + z-index: 12; } + +.reveal.page .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0, 0, 0, 0.1); + -webkit-transform: translateZ(-20px); + transform: translateZ(-20px); } + +.reveal.page .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2); + -webkit-transform: translateZ(-90px) rotateX(65deg); } + +.reveal.page .slides > section.stack { + padding: 0; + background: none; } + +.reveal.page .slides > section.past { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg); + transform: translate3d(-40%, 0, 0) rotateY(-80deg); } + +.reveal.page .slides > section.future { + -webkit-transform-origin: 100% 0%; + transform-origin: 100% 0%; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.reveal.page .slides > section > section.past { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg); + transform: translate3d(0, -40%, 0) rotateX(80deg); } + +.reveal.page .slides > section > section.future { + -webkit-transform-origin: 0% 100%; + transform-origin: 0% 100%; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +/********************************************* + * FADE TRANSITION + *********************************************/ +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides > section > section:not([data-transition]) { + -webkit-transform: none; + transform: none; + transition: opacity 0.5s; } + +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides > section > section { + transition: none; } + +/********************************************* + * NO TRANSITION + *********************************************/ +.reveal .slides section[data-transition=none], +.reveal.none .slides section:not([data-transition]) { + -webkit-transform: none; + transform: none; + transition: none; } + +/********************************************* + * PAUSED MODE + *********************************************/ +.reveal .pause-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + visibility: hidden; + opacity: 0; + z-index: 100; + transition: all 1s ease; } + +.reveal.paused .pause-overlay { + visibility: visible; + opacity: 1; } + +/********************************************* + * FALLBACK + *********************************************/ +.no-transforms { + overflow-y: auto; } + +.no-transforms .reveal .slides { + position: relative; + width: 80%; + height: auto !important; + top: 0; + left: 50%; + margin: 0; + text-align: center; } + +.no-transforms .reveal .controls, +.no-transforms .reveal .progress { + display: none !important; } + +.no-transforms .reveal .slides section { + display: block !important; + opacity: 1 !important; + position: relative !important; + height: auto; + min-height: 0; + top: 0; + left: -50%; + margin: 70px 0; + -webkit-transform: none; + transform: none; } + +.no-transforms .reveal .slides section section { + left: 0; } + +.reveal .no-transition, +.reveal .no-transition * { + transition: none !important; } + +/********************************************* + * PER-SLIDE BACKGROUNDS + *********************************************/ +.reveal .backgrounds { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + -webkit-perspective: 600px; + perspective: 600px; } + +.reveal .slide-background { + display: none; + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + overflow: hidden; + background-color: transparent; + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal .slide-background.stack { + display: block; } + +.reveal .slide-background.present { + opacity: 1; + visibility: visible; + z-index: 2; } + +.print-pdf .reveal .slide-background { + opacity: 1 !important; + visibility: visible !important; } + +/* Video backgrounds */ +.reveal .slide-background video { + position: absolute; + width: 100%; + height: 100%; + max-width: none; + max-height: none; + top: 0; + left: 0; + -o-object-fit: cover; + object-fit: cover; } + +.reveal .slide-background[data-background-size="contain"] video { + -o-object-fit: contain; + object-fit: contain; } + +/* Immediate transition style */ +.reveal[data-background-transition=none] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=none] { + transition: none; } + +/* Slide */ +.reveal[data-background-transition=slide] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=slide] { + opacity: 1; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=slide] { + -webkit-transform: translate(-100%, 0); + transform: translate(-100%, 0); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=slide] { + -webkit-transform: translate(100%, 0); + transform: translate(100%, 0); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] { + -webkit-transform: translate(0, -100%); + transform: translate(0, -100%); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] { + -webkit-transform: translate(0, 100%); + transform: translate(0, 100%); } + +/* Convex */ +.reveal[data-background-transition=convex] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); } + +/* Concave */ +.reveal[data-background-transition=concave] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); } + +/* Zoom */ +.reveal[data-background-transition=zoom] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] > .backgrounds .slide-background { + transition-duration: 400ms; } + +.reveal[data-transition-speed="slow"] > .backgrounds .slide-background { + transition-duration: 1200ms; } + +/********************************************* + * OVERVIEW + *********************************************/ +.reveal.overview { + -webkit-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; + -webkit-perspective: 700px; + perspective: 700px; } + .reveal.overview .slides { + -moz-transform-style: preserve-3d; } + .reveal.overview .slides section { + height: 100%; + top: 0 !important; + opacity: 1 !important; + overflow: hidden; + visibility: visible !important; + cursor: pointer; + box-sizing: border-box; } + .reveal.overview .slides section:hover, + .reveal.overview .slides section.present { + outline: 10px solid rgba(150, 150, 150, 0.4); + outline-offset: 10px; } + .reveal.overview .slides section .fragment { + opacity: 1; + transition: none; } + .reveal.overview .slides section:after, + .reveal.overview .slides section:before { + display: none !important; } + .reveal.overview .slides > section.stack { + padding: 0; + top: 0 !important; + background: none; + outline: none; + overflow: visible; } + .reveal.overview .backgrounds { + -webkit-perspective: inherit; + perspective: inherit; + -moz-transform-style: preserve-3d; } + .reveal.overview .backgrounds .slide-background { + opacity: 1; + visibility: visible; + outline: 10px solid rgba(150, 150, 150, 0.1); + outline-offset: 10px; } + .reveal.overview .backgrounds .slide-background.stack { + overflow: visible; } + +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; } + +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; } + +/********************************************* + * RTL SUPPORT + *********************************************/ +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { + direction: rtl; + font-family: sans-serif; } + +.reveal.rtl pre, +.reveal.rtl code { + direction: ltr; } + +.reveal.rtl ol, +.reveal.rtl ul { + text-align: right; } + +.reveal.rtl .progress span { + float: right; } + +/********************************************* + * PARALLAX BACKGROUND + *********************************************/ +.reveal.has-parallax-background .backgrounds { + transition: all 0.8s ease; } + +/* Global transition speed settings */ +.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { + transition-duration: 400ms; } + +.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { + transition-duration: 1200ms; } + +/********************************************* + * LINK PREVIEW OVERLAY + *********************************************/ +.reveal .overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + background: rgba(0, 0, 0, 0.9); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; } + +.reveal .overlay.visible { + opacity: 1; + visibility: visible; } + +.reveal .overlay .spinner { + position: absolute; + display: block; + top: 50%; + left: 50%; + width: 32px; + height: 32px; + margin: -16px 0 0 -16px; + z-index: 10; + background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); + visibility: visible; + opacity: 0.6; + transition: all 0.3s ease; } + +.reveal .overlay header { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 40px; + z-index: 2; + border-bottom: 1px solid #222; } + +.reveal .overlay header a { + display: inline-block; + width: 40px; + height: 40px; + line-height: 36px; + padding: 0 10px; + float: right; + opacity: 0.6; + box-sizing: border-box; } + +.reveal .overlay header a:hover { + opacity: 1; } + +.reveal .overlay header a .icon { + display: inline-block; + width: 20px; + height: 20px; + background-position: 50% 50%; + background-size: 100%; + background-repeat: no-repeat; } + +.reveal .overlay header a.close .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); } + +.reveal .overlay header a.external .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); } + +.reveal .overlay .viewport { + position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + top: 40px; + right: 0; + bottom: 0; + left: 0; } + +.reveal .overlay.overlay-preview .viewport iframe { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: 0; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; } + +.reveal .overlay.overlay-preview.loaded .viewport iframe { + opacity: 1; + visibility: visible; } + +.reveal .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; } + +.reveal .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; } + +.reveal .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; } + +.reveal .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; } + +.reveal .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; } + +.reveal .overlay.overlay-help .viewport .viewport-inner .title { + font-size: 20px; } + +.reveal .overlay.overlay-help .viewport .viewport-inner table { + border: 1px solid #fff; + border-collapse: collapse; + font-size: 16px; } + +.reveal .overlay.overlay-help .viewport .viewport-inner table th, +.reveal .overlay.overlay-help .viewport .viewport-inner table td { + width: 200px; + padding: 14px; + border: 1px solid #fff; + vertical-align: middle; } + +.reveal .overlay.overlay-help .viewport .viewport-inner table th { + padding-top: 20px; + padding-bottom: 20px; } + +/********************************************* + * PLAYBACK COMPONENT + *********************************************/ +.reveal .playback { + position: absolute; + left: 15px; + bottom: 20px; + z-index: 30; + cursor: pointer; + transition: all 400ms ease; + -webkit-tap-highlight-color: transparent; } + +.reveal.overview .playback { + opacity: 0; + visibility: hidden; } + +/********************************************* + * ROLLING LINKS + *********************************************/ +.reveal .roll { + display: inline-block; + line-height: 1.2; + overflow: hidden; + vertical-align: top; + -webkit-perspective: 400px; + perspective: 400px; + -webkit-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; } + +.reveal .roll:hover { + background: none; + text-shadow: none; } + +.reveal .roll span { + display: block; + position: relative; + padding: 0 2px; + pointer-events: none; + transition: all 400ms ease; + -webkit-transform-origin: 50% 0%; + transform-origin: 50% 0%; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .roll:hover span { + background: rgba(0, 0, 0, 0.5); + -webkit-transform: translate3d(0px, 0px, -45px) rotateX(90deg); + transform: translate3d(0px, 0px, -45px) rotateX(90deg); } + +.reveal .roll span:after { + content: attr(data-title); + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0 2px; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform-origin: 50% 0%; + transform-origin: 50% 0%; + -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg); + transform: translate3d(0px, 110%, 0px) rotateX(-90deg); } + +/********************************************* + * SPEAKER NOTES + *********************************************/ +.reveal aside.notes { + display: none; } + +.reveal .speaker-notes { + display: none; + position: absolute; + width: 25vw; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba(0, 0, 0, 0.05); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; } + .reveal .speaker-notes .notes-placeholder { + color: #ccc; + font-style: italic; } + .reveal .speaker-notes:focus { + outline: none; } + .reveal .speaker-notes:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; } + +.reveal.show-notes { + max-width: 75vw; + overflow: visible; } + +.reveal.show-notes .speaker-notes { + display: block; } + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; } } + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + overflow: visible; } + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: 42.8571428571%; } } + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; } + .reveal.show-notes .speaker-notes { + top: 100%; + height: 66.6666666667%; } + .reveal .speaker-notes { + font-size: 14px; } } + +/********************************************* + * ZOOM PLUGIN + *********************************************/ +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; } + +.zoomed .reveal .progress, +.zoomed .reveal .controls { + opacity: 0; } + +.zoomed .reveal .roll span { + background: none; } + +.zoomed .reveal .roll span:after { + visibility: hidden; } diff --git a/WebOversimplified/libs/revealjs/css/reveal.scss b/WebOversimplified/libs/revealjs/css/reveal.scss new file mode 100644 index 0000000..1a87624 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/reveal.scss @@ -0,0 +1,1717 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2017 Hakim El Hattab, http://hakim.se + */ + + +/********************************************* + * RESET STYLES + *********************************************/ + +html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, +.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, +.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, +.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, +.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, +.reveal b, .reveal u, .reveal center, +.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, +.reveal fieldset, .reveal form, .reveal label, .reveal legend, +.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, +.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, +.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, +.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, +.reveal time, .reveal mark, .reveal audio, .reveal video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, +.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { + display: block; +} + + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +html, +body { + width: 100%; + height: 100%; + overflow: hidden; +} + +body { + position: relative; + line-height: 1; + + background-color: #fff; + color: #000; +} + + +/********************************************* + * VIEW FRAGMENTS + *********************************************/ + +.reveal .slides section .fragment { + opacity: 0; + visibility: hidden; + transition: all .2s ease; + + &.visible { + opacity: 1; + visibility: inherit; + } +} + +.reveal .slides section .fragment.grow { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 1.3 ); + } +} + +.reveal .slides section .fragment.shrink { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 0.7 ); + } +} + +.reveal .slides section .fragment.zoom-in { + transform: scale( 0.1 ); + + &.visible { + transform: none; + } +} + +.reveal .slides section .fragment.fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0; + visibility: hidden; + } +} + +.reveal .slides section .fragment.semi-fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0.5; + visibility: inherit; + } +} + +.reveal .slides section .fragment.strike { + opacity: 1; + visibility: inherit; + + &.visible { + text-decoration: line-through; + } +} + +.reveal .slides section .fragment.fade-up { + transform: translate(0, 20%); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-down { + transform: translate(0, -20%); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-right { + transform: translate(-20%, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-left { + transform: translate(20%, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.current-visible { + opacity: 0; + visibility: hidden; + + &.current-fragment { + opacity: 1; + visibility: inherit; + } +} + +.reveal .slides section .fragment.highlight-red, +.reveal .slides section .fragment.highlight-current-red, +.reveal .slides section .fragment.highlight-green, +.reveal .slides section .fragment.highlight-current-green, +.reveal .slides section .fragment.highlight-blue, +.reveal .slides section .fragment.highlight-current-blue { + opacity: 1; + visibility: inherit; +} + .reveal .slides section .fragment.highlight-red.visible { + color: #ff2c2d + } + .reveal .slides section .fragment.highlight-green.visible { + color: #17ff2e; + } + .reveal .slides section .fragment.highlight-blue.visible { + color: #1b91ff; + } + +.reveal .slides section .fragment.highlight-current-red.current-fragment { + color: #ff2c2d +} +.reveal .slides section .fragment.highlight-current-green.current-fragment { + color: #17ff2e; +} +.reveal .slides section .fragment.highlight-current-blue.current-fragment { + color: #1b91ff; +} + + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ + +/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ +.reveal:after { + content: ''; + font-style: italic; +} + +.reveal iframe { + z-index: 1; +} + +/** Prevents layering issues in certain browser/transition combinations */ +.reveal a { + position: relative; +} + +.reveal .stretch { + max-width: none; + max-height: none; +} + +.reveal pre.stretch code { + height: 100%; + max-height: 100%; + box-sizing: border-box; +} + + +/********************************************* + * CONTROLS + *********************************************/ + +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(10px);} + 30% {transform: translateX(-5px);} +} + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% {transform: translateY(0);} + 20% {transform: translateY(10px);} + 30% {transform: translateY(-5px);} +} + +$controlArrowSize: 3.6em; +$controlArrowSpacing: 1.4em; +$controlArrowLength: 2.6em; +$controlArrowThickness: 0.5em; +$controlsArrowAngle: 45deg; +$controlsArrowAngleHover: 40deg; +$controlsArrowAngleActive: 36deg; + +@mixin controlsArrowTransform( $angle ) { + &:before { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( $angle ); + } + + &:after { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( -$angle ); + } +} + +.reveal .controls { + $spacing: 12px; + + display: none; + position: absolute; + top: auto; + bottom: $spacing; + right: $spacing; + left: auto; + z-index: 1; + color: #000; + pointer-events: none; + font-size: 10px; + + button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + transform: scale(.9999); + transition: color 0.2s ease, + opacity 0.2s ease, + transform 0.2s ease; + z-index: 2; // above slides + pointer-events: auto; + font-size: inherit; + + visibility: hidden; + opacity: 0; + + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + } + + .controls-arrow:before, + .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: $controlArrowLength; + height: $controlArrowThickness; + border-radius: $controlArrowThickness/2; + background-color: currentColor; + + transition: all 0.15s ease, background-color 0.8s ease; + transform-origin: floor(($controlArrowThickness/2)*10)/10 50%; + will-change: transform; + } + + .controls-arrow { + position: relative; + width: $controlArrowSize; + height: $controlArrowSize; + + @include controlsArrowTransform( $controlsArrowAngle ); + + &:hover { + @include controlsArrowTransform( $controlsArrowAngleHover ); + } + + &:active { + @include controlsArrowTransform( $controlsArrowAngleActive ); + } + } + + .navigate-left { + right: $controlArrowSize + $controlArrowSpacing*2; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( -10px ); + } + + .navigate-right { + right: 0; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( 10px ); + + .controls-arrow { + transform: rotate( 180deg ); + } + + &.highlight { + animation: bounce-right 2s 50 both ease-out; + } + } + + .navigate-up { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: $controlArrowSpacing*2 + $controlArrowSize; + transform: translateY( -10px ); + + .controls-arrow { + transform: rotate( 90deg ); + } + } + + .navigate-down { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: 0; + transform: translateY( 10px ); + + .controls-arrow { + transform: rotate( -90deg ); + } + + &.highlight { + animation: bounce-down 2s 50 both ease-out; + } + } + + // Back arrow style: "faded": + // Deemphasize backwards navigation arrows in favor of drawing + // attention to forwards navigation + &[data-controls-back-arrows="faded"] .navigate-left.enabled, + &[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show arrows for backwards navigation + &[data-controls-back-arrows="hidden"] .navigate-left.enabled, + &[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; + } + + // Any control button that can be clicked is "enabled" + .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + transform: none; + } + + // Any control button that leads to showing or hiding + // a fragment + .enabled.fragmented { + opacity: 0.5; + } + + .enabled:hover, + .enabled.fragmented:hover { + opacity: 1; + } +} + +// Adjust the layout when there are no vertical slides +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: $controlArrowSpacing; + right: 0.5em + $controlArrowSpacing + $controlArrowSize; +} + +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: $controlArrowSpacing; + right: 0.5em; +} + +// Adjust the layout when there are no horizontal slides +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: $controlArrowSpacing; + bottom: $controlArrowSpacing + $controlArrowSize; +} +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: $controlArrowSpacing; + bottom: 0.5em; +} + +// Invert arrows based on background color +.reveal.has-dark-background .controls { + color: #fff; +} +.reveal.has-light-background .controls { + color: #000; +} + +// Disable active states on touch devices +.reveal.no-hover .controls .controls-arrow:hover, +.reveal.no-hover .controls .controls-arrow:active { + @include controlsArrowTransform( $controlsArrowAngle ); +} + +// Edge aligned controls layout +@media screen and (min-width: 500px) { + + $spacing: 8px; + + .reveal .controls[data-controls-layout="edges"] { + & { + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + .navigate-left, + .navigate-right, + .navigate-up, + .navigate-down { + bottom: auto; + right: auto; + } + + .navigate-left { + top: 50%; + left: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-right { + top: 50%; + right: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-up { + top: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; + } + + .navigate-down { + bottom: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; + } + } + +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + position: absolute; + display: none; + height: 3px; + width: 100%; + bottom: 0; + left: 0; + z-index: 10; + + background-color: rgba( 0, 0, 0, 0.2 ); + color: #fff; +} + .reveal .progress:after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + top: -10px; + } + .reveal .progress span { + display: block; + height: 100%; + width: 0px; + + background-color: currentColor; + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + +/********************************************* + * SLIDE NUMBER + *********************************************/ + +.reveal .slide-number { + position: fixed; + display: block; + right: 8px; + bottom: 8px; + z-index: 31; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba( 0, 0, 0, 0.4 ); + padding: 5px; +} + +.reveal .slide-number-delimiter { + margin: 0 3px; +} + +/********************************************* + * SLIDES + *********************************************/ + +.reveal { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + touch-action: none; +} + +// Mobile Safari sometimes overlays a header at the top +// of the page when in landscape mode. Using fixed +// positioning ensures that reveal.js reduces its height +// when this header is visible. +@media only screen and (orientation : landscape) { + .reveal.ua-iphone { + position: fixed; + } +} + +.reveal .slides { + position: absolute; + width: 100%; + height: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + pointer-events: none; + + overflow: visible; + z-index: 1; + text-align: center; + perspective: 600px; + perspective-origin: 50% 40%; +} + +.reveal .slides>section { + -ms-perspective: 600px; +} + +.reveal .slides>section, +.reveal .slides>section>section { + display: none; + position: absolute; + width: 100%; + padding: 20px 0px; + pointer-events: auto; + + z-index: 10; + transform-style: flat; + transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); +} + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] .slides section { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"] .slides section { + transition-duration: 1200ms; +} + +/* Slide-specific transition speed overrides */ +.reveal .slides section[data-transition-speed="fast"] { + transition-duration: 400ms; +} +.reveal .slides section[data-transition-speed="slow"] { + transition-duration: 1200ms; +} + +.reveal .slides>section.stack { + padding-top: 0; + padding-bottom: 0; +} + +.reveal .slides>section.present, +.reveal .slides>section>section.present { + display: block; + z-index: 11; + opacity: 1; +} + +.reveal .slides>section:empty, +.reveal .slides>section>section:empty, +.reveal .slides>section[data-background-interactive], +.reveal .slides>section>section[data-background-interactive] { + pointer-events: none; +} + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { + min-height: 0 !important; +} + +/* Don't allow interaction with invisible slides */ +.reveal .slides>section.future, +.reveal .slides>section>section.future, +.reveal .slides>section.past, +.reveal .slides>section>section.past { + pointer-events: none; +} + +.reveal.overview .slides>section, +.reveal.overview .slides>section>section { + pointer-events: auto; +} + +.reveal .slides>section.past, +.reveal .slides>section.future, +.reveal .slides>section>section.past, +.reveal .slides>section>section.future { + opacity: 0; +} + + +/********************************************* + * Mixins for readability of transitions + *********************************************/ + +@mixin transition-global($style) { + .reveal .slides section[data-transition=#{$style}], + .reveal.#{$style} .slides section:not([data-transition]) { + @content; + } +} +@mixin transition-stack($style) { + .reveal .slides section[data-transition=#{$style}].stack, + .reveal.#{$style} .slides section.stack { + @content; + } +} +@mixin transition-horizontal-past($style) { + .reveal .slides>section[data-transition=#{$style}].past, + .reveal .slides>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section:not([data-transition]).past { + @content; + } +} +@mixin transition-horizontal-future($style) { + .reveal .slides>section[data-transition=#{$style}].future, + .reveal .slides>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section:not([data-transition]).future { + @content; + } +} + +@mixin transition-vertical-past($style) { + .reveal .slides>section>section[data-transition=#{$style}].past, + .reveal .slides>section>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section>section:not([data-transition]).past { + @content; + } +} +@mixin transition-vertical-future($style) { + .reveal .slides>section>section[data-transition=#{$style}].future, + .reveal .slides>section>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section>section:not([data-transition]).future { + @content; + } +} + +/********************************************* + * SLIDE TRANSITION + * Aliased 'linear' for backwards compatibility + *********************************************/ + +@each $stylename in slide, linear { + .reveal.#{$stylename} section { + backface-visibility: hidden; + } + @include transition-horizontal-past(#{$stylename}) { + transform: translate(-150%, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate(150%, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate(0, -150%); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate(0, 150%); + } +} + +/********************************************* + * CONVEX TRANSITION + * Aliased 'default' for backwards compatibility + *********************************************/ + +@each $stylename in default, convex { + @include transition-stack(#{$stylename}) { + transform-style: preserve-3d; + } + + @include transition-horizontal-past(#{$stylename}) { + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + } +} + +/********************************************* + * CONCAVE TRANSITION + *********************************************/ + +@include transition-stack(concave) { + transform-style: preserve-3d; +} + +@include transition-horizontal-past(concave) { + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +@include transition-horizontal-future(concave) { + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} +@include transition-vertical-past(concave) { + transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); +} +@include transition-vertical-future(concave) { + transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); +} + + +/********************************************* + * ZOOM TRANSITION + *********************************************/ + +@include transition-global(zoom) { + transition-timing-function: ease; +} +@include transition-horizontal-past(zoom) { + visibility: hidden; + transform: scale(16); +} +@include transition-horizontal-future(zoom) { + visibility: hidden; + transform: scale(0.2); +} +@include transition-vertical-past(zoom) { + transform: translate(0, -150%); +} +@include transition-vertical-future(zoom) { + transform: translate(0, 150%); +} + + +/********************************************* + * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.cube .slides { + perspective: 1300px; +} + +.reveal.cube .slides section { + padding: 30px; + min-height: 700px; + backface-visibility: hidden; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.center.cube .slides section { + min-height: 0; + } + .reveal.cube .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + border-radius: 4px; + transform: translateZ( -20px ); + } + .reveal.cube .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.cube .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.cube .slides>section.past { + transform-origin: 100% 0%; + transform: translate3d(-100%, 0, 0) rotateY(-90deg); +} + +.reveal.cube .slides>section.future { + transform-origin: 0% 0%; + transform: translate3d(100%, 0, 0) rotateY(90deg); +} + +.reveal.cube .slides>section>section.past { + transform-origin: 0% 100%; + transform: translate3d(0, -100%, 0) rotateX(90deg); +} + +.reveal.cube .slides>section>section.future { + transform-origin: 0% 0%; + transform: translate3d(0, 100%, 0) rotateX(-90deg); +} + + +/********************************************* + * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.page .slides { + perspective-origin: 0% 50%; + perspective: 3000px; +} + +.reveal.page .slides section { + padding: 30px; + min-height: 700px; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.page .slides section.past { + z-index: 12; + } + .reveal.page .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + transform: translateZ( -20px ); + } + .reveal.page .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + + -webkit-transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.page .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.page .slides>section.past { + transform-origin: 0% 0%; + transform: translate3d(-40%, 0, 0) rotateY(-80deg); +} + +.reveal.page .slides>section.future { + transform-origin: 100% 0%; + transform: translate3d(0, 0, 0); +} + +.reveal.page .slides>section>section.past { + transform-origin: 0% 0%; + transform: translate3d(0, -40%, 0) rotateX(80deg); +} + +.reveal.page .slides>section>section.future { + transform-origin: 0% 100%; + transform: translate3d(0, 0, 0); +} + + +/********************************************* + * FADE TRANSITION + *********************************************/ + +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides>section>section:not([data-transition]) { + transform: none; + transition: opacity 0.5s; +} + + +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides>section>section { + transition: none; +} + + +/********************************************* + * NO TRANSITION + *********************************************/ + +@include transition-global(none) { + transform: none; + transition: none; +} + + +/********************************************* + * PAUSED MODE + *********************************************/ + +.reveal .pause-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + visibility: hidden; + opacity: 0; + z-index: 100; + transition: all 1s ease; +} +.reveal.paused .pause-overlay { + visibility: visible; + opacity: 1; +} + + +/********************************************* + * FALLBACK + *********************************************/ + +.no-transforms { + overflow-y: auto; +} + +.no-transforms .reveal .slides { + position: relative; + width: 80%; + height: auto !important; + top: 0; + left: 50%; + margin: 0; + text-align: center; +} + +.no-transforms .reveal .controls, +.no-transforms .reveal .progress { + display: none !important; +} + +.no-transforms .reveal .slides section { + display: block !important; + opacity: 1 !important; + position: relative !important; + height: auto; + min-height: 0; + top: 0; + left: -50%; + margin: 70px 0; + transform: none; +} + +.no-transforms .reveal .slides section section { + left: 0; +} + +.reveal .no-transition, +.reveal .no-transition * { + transition: none !important; +} + + +/********************************************* + * PER-SLIDE BACKGROUNDS + *********************************************/ + +.reveal .backgrounds { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + perspective: 600px; +} + .reveal .slide-background { + display: none; + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + overflow: hidden; + + background-color: rgba( 0, 0, 0, 0 ); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background.stack { + display: block; + } + + .reveal .slide-background.present { + opacity: 1; + visibility: visible; + z-index: 2; + } + + .print-pdf .reveal .slide-background { + opacity: 1 !important; + visibility: visible !important; + } + +/* Video backgrounds */ +.reveal .slide-background video { + position: absolute; + width: 100%; + height: 100%; + max-width: none; + max-height: none; + top: 0; + left: 0; + object-fit: cover; +} + .reveal .slide-background[data-background-size="contain"] video { + object-fit: contain; + } + +/* Immediate transition style */ +.reveal[data-background-transition=none]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=none] { + transition: none; +} + +/* Slide */ +.reveal[data-background-transition=slide]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=slide] { + opacity: 1; + backface-visibility: hidden; +} + .reveal[data-background-transition=slide]>.backgrounds .slide-background.past, + .reveal>.backgrounds .slide-background.past[data-background-transition=slide] { + transform: translate(-100%, 0); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background.future, + .reveal>.backgrounds .slide-background.future[data-background-transition=slide] { + transform: translate(100%, 0); + } + + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past, + .reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide] { + transform: translate(0, -100%); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future, + .reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide] { + transform: translate(0, 100%); + } + + +/* Convex */ +.reveal[data-background-transition=convex]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); +} + + +/* Concave */ +.reveal[data-background-transition=concave]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); +} + +/* Zoom */ +.reveal[data-background-transition=zoom]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"]>.backgrounds .slide-background { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"]>.backgrounds .slide-background { + transition-duration: 1200ms; +} + + +/********************************************* + * OVERVIEW + *********************************************/ + +.reveal.overview { + perspective-origin: 50% 50%; + perspective: 700px; + + .slides { + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .slides section { + height: 100%; + top: 0 !important; + opacity: 1 !important; + overflow: hidden; + visibility: visible !important; + cursor: pointer; + box-sizing: border-box; + } + .slides section:hover, + .slides section.present { + outline: 10px solid rgba(150,150,150,0.4); + outline-offset: 10px; + } + .slides section .fragment { + opacity: 1; + transition: none; + } + .slides section:after, + .slides section:before { + display: none !important; + } + .slides>section.stack { + padding: 0; + top: 0 !important; + background: none; + outline: none; + overflow: visible; + } + + .backgrounds { + perspective: inherit; + + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .backgrounds .slide-background { + opacity: 1; + visibility: visible; + + // This can't be applied to the slide itself in Safari + outline: 10px solid rgba(150,150,150,0.1); + outline-offset: 10px; + } + + .backgrounds .slide-background.stack { + overflow: visible; + } +} + +// Disable transitions transitions while we're activating +// or deactivating the overview mode. +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; +} + +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; +} + + +/********************************************* + * RTL SUPPORT + *********************************************/ + +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { + direction: rtl; + font-family: sans-serif; +} + +.reveal.rtl pre, +.reveal.rtl code { + direction: ltr; +} + +.reveal.rtl ol, +.reveal.rtl ul { + text-align: right; +} + +.reveal.rtl .progress span { + float: right +} + +/********************************************* + * PARALLAX BACKGROUND + *********************************************/ + +.reveal.has-parallax-background .backgrounds { + transition: all 0.8s ease; +} + +/* Global transition speed settings */ +.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { + transition-duration: 400ms; +} +.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { + transition-duration: 1200ms; +} + + +/********************************************* + * LINK PREVIEW OVERLAY + *********************************************/ + +.reveal .overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + background: rgba( 0, 0, 0, 0.9 ); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + .reveal .overlay.visible { + opacity: 1; + visibility: visible; + } + + .reveal .overlay .spinner { + position: absolute; + display: block; + top: 50%; + left: 50%; + width: 32px; + height: 32px; + margin: -16px 0 0 -16px; + z-index: 10; + background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); + + visibility: visible; + opacity: 0.6; + transition: all 0.3s ease; + } + + .reveal .overlay header { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 40px; + z-index: 2; + border-bottom: 1px solid #222; + } + .reveal .overlay header a { + display: inline-block; + width: 40px; + height: 40px; + line-height: 36px; + padding: 0 10px; + float: right; + opacity: 0.6; + + box-sizing: border-box; + } + .reveal .overlay header a:hover { + opacity: 1; + } + .reveal .overlay header a .icon { + display: inline-block; + width: 20px; + height: 20px; + + background-position: 50% 50%; + background-size: 100%; + background-repeat: no-repeat; + } + .reveal .overlay header a.close .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); + } + .reveal .overlay header a.external .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); + } + + .reveal .overlay .viewport { + position: absolute; + display: flex; + top: 40px; + right: 0; + bottom: 0; + left: 0; + } + + .reveal .overlay.overlay-preview .viewport iframe { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: 0; + + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .reveal .overlay.overlay-preview.loaded .viewport iframe { + opacity: 1; + visibility: visible; + } + + .reveal .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; + } + .reveal .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; + } + .reveal .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; + } + + .reveal .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + transform: scale(0.2); + } + + .reveal .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; + } + + .reveal .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; + } + + .reveal .overlay.overlay-help .viewport .viewport-inner .title { + font-size: 20px; + } + + .reveal .overlay.overlay-help .viewport .viewport-inner table { + border: 1px solid #fff; + border-collapse: collapse; + font-size: 16px; + } + + .reveal .overlay.overlay-help .viewport .viewport-inner table th, + .reveal .overlay.overlay-help .viewport .viewport-inner table td { + width: 200px; + padding: 14px; + border: 1px solid #fff; + vertical-align: middle; + } + + .reveal .overlay.overlay-help .viewport .viewport-inner table th { + padding-top: 20px; + padding-bottom: 20px; + } + + + +/********************************************* + * PLAYBACK COMPONENT + *********************************************/ + +.reveal .playback { + position: absolute; + left: 15px; + bottom: 20px; + z-index: 30; + cursor: pointer; + transition: all 400ms ease; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); +} + +.reveal.overview .playback { + opacity: 0; + visibility: hidden; +} + + +/********************************************* + * ROLLING LINKS + *********************************************/ + +.reveal .roll { + display: inline-block; + line-height: 1.2; + overflow: hidden; + + vertical-align: top; + perspective: 400px; + perspective-origin: 50% 50%; +} + .reveal .roll:hover { + background: none; + text-shadow: none; + } +.reveal .roll span { + display: block; + position: relative; + padding: 0 2px; + + pointer-events: none; + transition: all 400ms ease; + transform-origin: 50% 0%; + transform-style: preserve-3d; + backface-visibility: hidden; +} + .reveal .roll:hover span { + background: rgba(0,0,0,0.5); + transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); + } +.reveal .roll span:after { + content: attr(data-title); + + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0 2px; + backface-visibility: hidden; + transform-origin: 50% 0%; + transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); +} + + +/********************************************* + * SPEAKER NOTES + *********************************************/ + +// Hide on-page notes +.reveal aside.notes { + display: none; +} + +// An interface element that can optionally be used to show the +// speaker notes to all viewers, on top of the presentation +.reveal .speaker-notes { + display: none; + position: absolute; + width: 25vw; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba( 0, 0, 0, 0.05 ); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; + + .notes-placeholder { + color: #ccc; + font-style: italic; + } + + &:focus { + outline: none; + } + + &:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; + } +} + + +.reveal.show-notes { + max-width: 75vw; + overflow: visible; +} + +.reveal.show-notes .speaker-notes { + display: block; +} + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; + } +} + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + overflow: visible; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: (30/0.7)*1%; + } +} + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + height: (40/0.6)*1%; + } + + .reveal .speaker-notes { + font-size: 14px; + } +} + + +/********************************************* + * ZOOM PLUGIN + *********************************************/ + +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { + backface-visibility: visible !important; +} + +.zoomed .reveal .progress, +.zoomed .reveal .controls { + opacity: 0; +} + +.zoomed .reveal .roll span { + background: none; +} + +.zoomed .reveal .roll span:after { + visibility: hidden; +} diff --git a/WebOversimplified/libs/revealjs/css/theme/README.md b/WebOversimplified/libs/revealjs/css/theme/README.md new file mode 100644 index 0000000..8ae164b --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/README.md @@ -0,0 +1,21 @@ +## Dependencies + +Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceeding: https://github.com/hakimel/reveal.js#full-setup + +## Creating a Theme + +To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js)) when you run `npm run build -- css-themes`. + +Each theme file does four things in the following order: + +1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)** +Shared utility functions. + +2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)** +Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3. + +3. **Override** +This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding any selectors and styles you please. + +4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** +The template theme file which will generate final CSS output based on the currently defined variables. diff --git a/WebOversimplified/libs/revealjs/css/theme/beige.css b/WebOversimplified/libs/revealjs/css/theme/beige.css new file mode 100644 index 0000000..d2d0ca6 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/beige.css @@ -0,0 +1,268 @@ +/** + * Beige theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #f7f2d3; + background: -moz-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, white), color-stop(100%, #f7f2d3)); + background: -webkit-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -o-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -ms-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background-color: #f7f3de; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #333; } + +::selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #333; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #8b743d; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #c0a86e; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #564826; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #333; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #8b743d; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #8b743d; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #8b743d; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/black-sha.css b/WebOversimplified/libs/revealjs/css/theme/black-sha.css new file mode 100644 index 0000000..b8b1c9a --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/black-sha.css @@ -0,0 +1,298 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ +/* @import url(../../lib/font/source-sans-pro/source-sans-pro.css); */ +@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900'); /* SHA */ +@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600,700'); /* SHA */ +section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { + color: #222; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #0D2234; /* SHA */ + background-color: #0D2234; /* SHA */ +} + +.reveal { + /*font-family: "Source Sans Pro", Helvetica, sans-serif;*/ + font-family: 'Roboto', sans-serif; + font-size: 42px; + font-weight: normal; + color: #fff; } + +::selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #fff; + /*font-family: "Source Sans Pro", Helvetica, sans-serif;*/ + font-family: 'Roboto', sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + /*text-transform: uppercase;*/ + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/* SHA */ +.reveal span.colorful, +.reveal strong.colorful, +.reveal h1.colorful, +.reveal h2.colorful, +.reveal h3.colorful, +.reveal h4.colorful, +.reveal h5.colorful, +.reveal h6.colorful { + color: #F7BE2C; +} + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + /*margin: 20px auto;*/ + margin: 0px auto; /* SHA */ + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + DISABLEDbox-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); + white-space: pre-line; /* SHA */ +} + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +.reveal ul.small li, +.reveal li.small { + font-size: 0.7em; +} + +.reveal .greyed_out { + opacity: 0.4; +} + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + /*color: #42affa;*/ + color: #F7BE2C; /* SHA */ + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #8dcffc; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #068de9; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a.black { + color: black; +} + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #42affa; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #42affa; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #42affa; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/black.css b/WebOversimplified/libs/revealjs/css/theme/black.css new file mode 100644 index 0000000..83f6eee --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/black.css @@ -0,0 +1,264 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { + color: #222; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #222; + background-color: #222; } + +.reveal { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; + font-weight: normal; + color: #fff; } + +::selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #fff; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #42affa; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #8dcffc; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #068de9; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #42affa; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #42affa; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #42affa; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/blood.css b/WebOversimplified/libs/revealjs/css/theme/blood.css new file mode 100644 index 0000000..30e4b09 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/blood.css @@ -0,0 +1,287 @@ +/** + * Blood theme for reveal.js + * Author: Walther http://github.com/Walther + * + * Designed to be used with highlight.js theme + * "monokai_sublime.css" available from + * https://github.com/isagalaev/highlight.js/ + * + * For other themes, change $codeBackground accordingly. + * + */ +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #222; + background-color: #222; } + +.reveal { + font-family: Ubuntu, "sans-serif"; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #a23; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #a23; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: Ubuntu, "sans-serif"; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: 2px 2px 2px #222; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #a23; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #dd5566; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #6a1520; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #a23; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #a23; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #a23; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal p { + font-weight: 300; + text-shadow: 1px 1px #222; } + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + font-weight: 700; } + +.reveal p code { + background-color: #23241f; + display: inline-block; + border-radius: 7px; } + +.reveal small code { + vertical-align: baseline; } diff --git a/WebOversimplified/libs/revealjs/css/theme/league.css b/WebOversimplified/libs/revealjs/css/theme/league.css new file mode 100644 index 0000000..e35d668 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/league.css @@ -0,0 +1,270 @@ +/** + * League theme for reveal.js. + * + * This was the default theme pre-3.0.0. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #1c1e20; + background: -moz-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #555a5f), color-stop(100%, #1c1e20)); + background: -webkit-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -o-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -ms-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background-color: #2b2b2b; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #13DAEC; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #71e9f4; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #0d99a5; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #13DAEC; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #13DAEC; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #13DAEC; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/moon.css b/WebOversimplified/libs/revealjs/css/theme/moon.css new file mode 100644 index 0000000..c7705d9 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/moon.css @@ -0,0 +1,268 @@ +/** + * Solarized Dark theme for reveal.js. + * Author: Achim Staebler + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #002b36; + background-color: #002b36; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #93a1a1; } + +::selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee8d5; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #268bd2; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #78b9e6; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a6091; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #93a1a1; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #268bd2; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #268bd2; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/night.css b/WebOversimplified/libs/revealjs/css/theme/night.css new file mode 100644 index 0000000..5aca9c0 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/night.css @@ -0,0 +1,262 @@ +/** + * Black theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #111; + background-color: #111; } + +.reveal { + font-family: "Open Sans", sans-serif; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: "Montserrat", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: -0.03em; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #e7ad52; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #f3d7ac; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #d08a1d; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #e7ad52; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #e7ad52; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #e7ad52; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/serif.css b/WebOversimplified/libs/revealjs/css/theme/serif.css new file mode 100644 index 0000000..72298a9 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/serif.css @@ -0,0 +1,264 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is brown. + * + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. + */ +.reveal a { + line-height: 1.3em; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #F0F1EB; + background-color: #F0F1EB; } + +.reveal { + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-size: 40px; + font-weight: normal; + color: #000; } + +::selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #383D3D; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #51483D; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #8b7c69; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #25211c; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #000; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #51483D; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #51483D; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #51483D; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/simple.css b/WebOversimplified/libs/revealjs/css/theme/simple.css new file mode 100644 index 0000000..40d1238 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/simple.css @@ -0,0 +1,267 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is darkblue. + * + * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fff; + background-color: #fff; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #000; } + +::selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #000; + font-family: "News Cycle", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #00008B; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #0000f1; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #00003f; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #000; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #00008B; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #00008B; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #00008B; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/sky.css b/WebOversimplified/libs/revealjs/css/theme/sky.css new file mode 100644 index 0000000..f4b0ad5 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/sky.css @@ -0,0 +1,271 @@ +/** + * Sky theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); +.reveal a { + line-height: 1.3em; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #add9e4; + background: -moz-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #f7fbfc), color-stop(100%, #add9e4)); + background: -webkit-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -o-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -ms-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background-color: #f7fbfc; } + +.reveal { + font-family: "Open Sans", sans-serif; + font-size: 40px; + font-weight: normal; + color: #333; } + +::selection { + color: #fff; + background: #134674; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #134674; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #333; + font-family: "Quicksand", sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: -0.08em; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #3b759e; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #74a7cb; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #264c66; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #333; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #3b759e; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #3b759e; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #3b759e; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/solarized.css b/WebOversimplified/libs/revealjs/css/theme/solarized.css new file mode 100644 index 0000000..3affa6e --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/solarized.css @@ -0,0 +1,268 @@ +/** + * Solarized Light theme for reveal.js. + * Author: Achim Staebler + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fdf6e3; + background-color: #fdf6e3; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #657b83; } + +::selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #586e75; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #268bd2; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #78b9e6; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a6091; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #657b83; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #268bd2; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #268bd2; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/css/theme/source/beige.scss b/WebOversimplified/libs/revealjs/css/theme/source/beige.scss new file mode 100644 index 0000000..5564f53 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/beige.scss @@ -0,0 +1,39 @@ +/** + * Beige theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainColor: #333; +$headingColor: #333; +$headingTextShadow: none; +$backgroundColor: #f7f3de; +$linkColor: #8b743d; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(79, 64, 28, 0.99); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/black.scss b/WebOversimplified/libs/revealjs/css/theme/source/black.scss new file mode 100644 index 0000000..84e8d9a --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/black.scss @@ -0,0 +1,49 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #222; + +$mainColor: #fff; +$headingColor: #fff; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #42affa; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +section.has-light-background { + &, h1, h2, h3, h4, h5, h6 { + color: #222; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/blood.scss b/WebOversimplified/libs/revealjs/css/theme/source/blood.scss new file mode 100644 index 0000000..4533fc0 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/blood.scss @@ -0,0 +1,78 @@ +/** + * Blood theme for reveal.js + * Author: Walther http://github.com/Walther + * + * Designed to be used with highlight.js theme + * "monokai_sublime.css" available from + * https://github.com/isagalaev/highlight.js/ + * + * For other themes, change $codeBackground accordingly. + * + */ + + // Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + +// Include theme-specific fonts + +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); + +// Colors used in the theme +$blood: #a23; +$coal: #222; +$codeBackground: #23241f; + +$backgroundColor: $coal; + +// Main text +$mainFont: Ubuntu, 'sans-serif'; +$mainColor: #eee; + +// Headings +$headingFont: Ubuntu, 'sans-serif'; +$headingTextShadow: 2px 2px 2px $coal; + +// h1 shadow, borrowed humbly from +// (c) Default theme by Hakim El Hattab +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Links +$linkColor: $blood; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: $blood; +$selectionColor: #fff; + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- + +// some overrides after theme template import + +.reveal p { + font-weight: 300; + text-shadow: 1px 1px $coal; +} + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + font-weight: 700; +} + +.reveal p code { + background-color: $codeBackground; + display: inline-block; + border-radius: 7px; +} + +.reveal small code { + vertical-align: baseline; +} \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/league.scss b/WebOversimplified/libs/revealjs/css/theme/source/league.scss new file mode 100644 index 0000000..46ea04a --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/league.scss @@ -0,0 +1,34 @@ +/** + * League theme for reveal.js. + * + * This was the default theme pre-3.0.0. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +// Override theme settings (see ../template/settings.scss) +$headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/moon.scss b/WebOversimplified/libs/revealjs/css/theme/source/moon.scss new file mode 100644 index 0000000..e47e5b5 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/moon.scss @@ -0,0 +1,57 @@ +/** + * Solarized Dark theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base1; +$headingColor: $base2; +$headingTextShadow: none; +$backgroundColor: $base03; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/WebOversimplified/libs/revealjs/css/theme/source/night.scss b/WebOversimplified/libs/revealjs/css/theme/source/night.scss new file mode 100644 index 0000000..d49a282 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/night.scss @@ -0,0 +1,34 @@ +/** + * Black theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #111; + +$mainFont: 'Open Sans', sans-serif; +$linkColor: #e7ad52; +$linkColorHover: lighten( $linkColor, 20% ); +$headingFont: 'Montserrat', Impact, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: -0.03em; +$headingTextTransform: none; +$selectionBackgroundColor: #e7ad52; + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/serif.scss b/WebOversimplified/libs/revealjs/css/theme/source/serif.scss new file mode 100644 index 0000000..ec3fcb3 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/serif.scss @@ -0,0 +1,35 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is brown. + * + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$mainColor: #000; +$headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$headingColor: #383D3D; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #F0F1EB; +$linkColor: #51483D; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #26351C; + +.reveal a { + line-height: 1.3em; +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/WebOversimplified/libs/revealjs/css/theme/source/simple.scss b/WebOversimplified/libs/revealjs/css/theme/source/simple.scss new file mode 100644 index 0000000..394c9cd --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/simple.scss @@ -0,0 +1,43 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is darkblue. + * + * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Lato', sans-serif; +$mainColor: #000; +$headingFont: 'News Cycle', Impact, sans-serif; +$headingColor: #000; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #fff; +$linkColor: #00008B; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(0, 0, 0, 0.99); + +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/source/sky.scss b/WebOversimplified/libs/revealjs/css/theme/source/sky.scss new file mode 100644 index 0000000..3fee67c --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/sky.scss @@ -0,0 +1,46 @@ +/** + * Sky theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Open Sans', sans-serif; +$mainColor: #333; +$headingFont: 'Quicksand', sans-serif; +$headingColor: #333; +$headingLetterSpacing: -0.08em; +$headingTextShadow: none; +$backgroundColor: #f7fbfc; +$linkColor: #3b759e; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #134674; + +// Fix links so they are not cut off +.reveal a { + line-height: 1.3em; +} + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( #add9e4, #f7fbfc ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/WebOversimplified/libs/revealjs/css/theme/source/solarized.scss b/WebOversimplified/libs/revealjs/css/theme/source/solarized.scss new file mode 100644 index 0000000..912be56 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/solarized.scss @@ -0,0 +1,63 @@ +/** + * Solarized Light theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base00; +$headingColor: $base01; +$headingTextShadow: none; +$backgroundColor: $base3; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + +// Background generator +// @mixin bodyBackground() { +// @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) ); +// } + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/WebOversimplified/libs/revealjs/css/theme/source/white.scss b/WebOversimplified/libs/revealjs/css/theme/source/white.scss new file mode 100644 index 0000000..7f06ffd --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/source/white.scss @@ -0,0 +1,49 @@ +/** + * White theme for reveal.js. This is the opposite of the 'black' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #fff; + +$mainColor: #222; +$headingColor: #222; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #2a76dd; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/template/mixins.scss b/WebOversimplified/libs/revealjs/css/theme/template/mixins.scss new file mode 100644 index 0000000..e0c5606 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/template/mixins.scss @@ -0,0 +1,29 @@ +@mixin vertical-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); + background: -o-linear-gradient( top, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); + background: linear-gradient( top, $top 0%, $bottom 100% ); +} + +@mixin horizontal-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); + background: -o-linear-gradient( left, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); + background: linear-gradient( left, $top 0%, $bottom 100% ); +} + +@mixin radial-gradient( $outer, $inner, $type: circle ) { + background: $outer; + background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); + background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); +} \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/template/settings.scss b/WebOversimplified/libs/revealjs/css/theme/template/settings.scss new file mode 100644 index 0000000..63c02cf --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/template/settings.scss @@ -0,0 +1,43 @@ +// Base settings for all themes that can optionally be +// overridden by the super-theme + +// Background of the presentation +$backgroundColor: #2b2b2b; + +// Primary/body text +$mainFont: 'Lato', sans-serif; +$mainFontSize: 40px; +$mainColor: #eee; + +// Vertical spacing between blocks of text +$blockMargin: 20px; + +// Headings +$headingMargin: 0 0 $blockMargin 0; +$headingFont: 'League Gothic', Impact, sans-serif; +$headingColor: #eee; +$headingLineHeight: 1.2; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingTextShadow: none; +$headingFontWeight: normal; +$heading1TextShadow: $headingTextShadow; + +$heading1Size: 3.77em; +$heading2Size: 2.11em; +$heading3Size: 1.55em; +$heading4Size: 1.00em; + +// Links and actions +$linkColor: #13DAEC; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: #FF5E99; +$selectionColor: #fff; + +// Generates the presentation background, can be overridden +// to return a background image or gradient +@mixin bodyBackground() { + background: $backgroundColor; +} \ No newline at end of file diff --git a/WebOversimplified/libs/revealjs/css/theme/template/theme.scss b/WebOversimplified/libs/revealjs/css/theme/template/theme.scss new file mode 100644 index 0000000..58dc727 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/template/theme.scss @@ -0,0 +1,316 @@ +// Base theme template for reveal.js + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +body { + @include bodyBackground(); + background-color: $backgroundColor; +} + +.reveal { + font-family: $mainFont; + font-size: $mainFontSize; + font-weight: normal; + color: $mainColor; +} + +::selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +::-moz-selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +.reveal .slides>section, +.reveal .slides>section>section { + line-height: 1.3; + font-weight: inherit; +} + +/********************************************* + * HEADERS + *********************************************/ + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: $headingMargin; + color: $headingColor; + + font-family: $headingFont; + font-weight: $headingFontWeight; + line-height: $headingLineHeight; + letter-spacing: $headingLetterSpacing; + + text-transform: $headingTextTransform; + text-shadow: $headingTextShadow; + + word-wrap: break-word; +} + +.reveal h1 {font-size: $heading1Size; } +.reveal h2 {font-size: $heading2Size; } +.reveal h3 {font-size: $heading3Size; } +.reveal h4 {font-size: $heading4Size; } + +.reveal h1 { + text-shadow: $heading1TextShadow; +} + + +/********************************************* + * OTHER + *********************************************/ + +.reveal p { + margin: $blockMargin 0; + line-height: 1.3; +} + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; +} +.reveal strong, +.reveal b { + font-weight: bold; +} + +.reveal em { + font-style: italic; +} + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + + text-align: left; + margin: 0 0 0 1em; +} + +.reveal ol { + list-style-type: decimal; +} + +.reveal ul { + list-style-type: disc; +} + +.reveal ul ul { + list-style-type: square; +} + +.reveal ul ul ul { + list-style-type: circle; +} + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; +} + +.reveal dt { + font-weight: bold; +} + +.reveal dd { + margin-left: 40px; +} + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: $blockMargin auto; + padding: 5px; + + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0,0,0,0.2); +} + .reveal blockquote p:first-child, + .reveal blockquote p:last-child { + display: inline-block; + } + +.reveal q { + font-style: italic; +} + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: $blockMargin auto; + + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + + word-wrap: break-word; + + box-shadow: 0px 0px 6px rgba(0,0,0,0.3); +} + +.reveal code { + font-family: monospace; + text-transform: none; +} + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; +} + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; +} + +.reveal table th { + font-weight: bold; +} + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; +} + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; +} + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; +} + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; +} + +.reveal sup { + vertical-align: super; +} +.reveal sub { + vertical-align: sub; +} + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; +} + +.reveal small * { + vertical-align: top; +} + + +/********************************************* + * LINKS + *********************************************/ + +.reveal a { + color: $linkColor; + text-decoration: none; + + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; +} + .reveal a:hover { + color: $linkColorHover; + + text-shadow: none; + border: none; + } + +.reveal .roll span:after { + color: #fff; + background: darken( $linkColor, 15% ); +} + + +/********************************************* + * IMAGES + *********************************************/ + +.reveal section img { + margin: 15px 0px; + background: rgba(255,255,255,0.12); + border: 4px solid $mainColor; + + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); +} + + .reveal section img.plain { + border: 0; + box-shadow: none; + } + + .reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; + } + + .reveal a:hover img { + background: rgba(255,255,255,0.2); + border-color: $linkColor; + + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); + } + + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ + +.reveal .controls { + color: $linkColor; +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + background: rgba(0,0,0,0.2); + color: $linkColor; +} + .reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + diff --git a/WebOversimplified/libs/revealjs/css/theme/white.css b/WebOversimplified/libs/revealjs/css/theme/white.css new file mode 100644 index 0000000..edc5a60 --- /dev/null +++ b/WebOversimplified/libs/revealjs/css/theme/white.css @@ -0,0 +1,264 @@ +/** + * White theme for reveal.js. This is the opposite of the 'black' theme. + * + * By Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fff; + background-color: #fff; } + +.reveal { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; + font-weight: normal; + color: #222; } + +::selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +.reveal .slides > section, +.reveal .slides > section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #222; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; } + +.reveal sub { + vertical-align: sub; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #2a76dd; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #6ca0e8; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a53a1; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #222; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #2a76dd; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #2a76dd; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #2a76dd; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } diff --git a/WebOversimplified/libs/revealjs/js/reveal.js b/WebOversimplified/libs/revealjs/js/reveal.js new file mode 100644 index 0000000..c371371 --- /dev/null +++ b/WebOversimplified/libs/revealjs/js/reveal.js @@ -0,0 +1,5241 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2017 Hakim El Hattab, http://hakim.se + */ +(function( root, factory ) { + if( typeof define === 'function' && define.amd ) { + // AMD. Register as an anonymous module. + define( function() { + root.Reveal = factory(); + return root.Reveal; + } ); + } else if( typeof exports === 'object' ) { + // Node. Does not work with strict CommonJS. + module.exports = factory(); + } else { + // Browser globals. + root.Reveal = factory(); + } +}( this, function() { + + 'use strict'; + + var Reveal; + + // The reveal.js version + var VERSION = '3.6.0'; + + var SLIDES_SELECTOR = '.slides section', + HORIZONTAL_SLIDES_SELECTOR = '.slides>section', + VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section', + HOME_SLIDE_SELECTOR = '.slides>section:first-of-type', + UA = navigator.userAgent, + + // Configuration defaults, can be overridden at initialization time + config = { + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.04, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 2.0, + + // Display presentation control arrows + controls: true, + + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + + // Display a presentation progress bar + progress: true, + + // Display the page number of the current slide + slideNumber: false, + + // Determine which displays to show the slide number on + showSlideNumber: 'all', + + // Push each slide change to the browser history + history: false, + + // Enable keyboard shortcuts for navigation + keyboard: true, + + // Optional function that blocks keyboard events when retuning false + keyboardCondition: null, + + // Enable the slide overview mode + overview: true, + + // Vertical centering of slides + center: true, + + // Enables touch navigation on devices with touch input + touch: true, + + // Loop the presentation + loop: false, + + // Change the presentation direction to be RTL + rtl: false, + + // Randomizes the order of slides each time the presentation loads + shuffle: false, + + // Turns fragments on and off globally + fragments: true, + + // Flags if the presentation is running in an embedded mode, + // i.e. contained within a limited portion of the screen + embedded: false, + + // Flags if we should show a help overlay when the question-mark + // key is pressed + help: true, + + // Flags if it should be possible to pause the presentation (blackout) + pause: true, + + // Flags if speaker notes should be visible to all viewers + showNotes: false, + + // Global override for autolaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Controls automatic progression to the next slide + // - 0: Auto-sliding only happens if the data-autoslide HTML attribute + // is present on the current slide or fragment + // - 1+: All slides will progress automatically at the given interval + // - false: No auto-sliding, even if data-autoslide is present + autoSlide: 0, + + // Stop auto-sliding after user input + autoSlideStoppable: true, + + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + + // Enable slide navigation via mouse wheel + mouseWheel: false, + + // Apply a 3D roll to links on hover + rollingLinks: false, + + // Hides the address bar on mobile devices + hideAddressBar: true, + + // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually + previewLinks: false, + + // Exposes the reveal.js API through window.postMessage + postMessage: true, + + // Dispatches all reveal.js events to the parent window through postMessage + postMessageEvents: false, + + // Focuses body when page changes visibility to ensure keyboard shortcuts work + focusBodyOnPageVisibilityChange: true, + + // Transition style + transition: 'slide', // none/fade/slide/convex/concave/zoom + + // Transition speed + transitionSpeed: 'default', // default/fast/slow + + // Transition style for full page slide backgrounds + backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom + + // Parallax background image + parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg" + + // Parallax background size + parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px" + + // Amount of pixels to move the parallax background per slide step + parallaxBackgroundHorizontal: null, + parallaxBackgroundVertical: null, + + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + + // Offset used to reduce the height of content within exported PDF pages. + // This exists to account for environment differences based on how you + // print to PDF. CLI printing options, like phantomjs and wkpdf, can end + // on precisely the total height of the document whereas in-browser + // printing has to end one pixel before. + pdfPageHeightOffset: -1, + + // Number of slides away from the current that are visible + viewDistance: 3, + + // The display mode that will be used to show slides + display: 'block', + + // Script dependencies to load + dependencies: [] + + }, + + // Flags if Reveal.initialize() has been called + initialized = false, + + // Flags if reveal.js is loaded (has dispatched the 'ready' event) + loaded = false, + + // Flags if the overview mode is currently active + overview = false, + + // Holds the dimensions of our overview slides, including margins + overviewSlideWidth = null, + overviewSlideHeight = null, + + // The horizontal and vertical index of the currently active slide + indexh, + indexv, + + // The previous and current slide HTML elements + previousSlide, + currentSlide, + + previousBackground, + + // Remember which directions that the user has navigated towards + hasNavigatedRight = false, + hasNavigatedDown = false, + + // Slides may hold a data-state attribute which we pick up and apply + // as a class to the body. This list contains the combined state of + // all current slides. + state = [], + + // The current scale of the presentation (see width/height config) + scale = 1, + + // CSS transform that is currently applied to the slides container, + // split into two groups + slidesTransform = { layout: '', overview: '' }, + + // Cached references to DOM elements + dom = {}, + + // Features supported by the browser, see #checkCapabilities() + features = {}, + + // Client is a mobile device, see #checkCapabilities() + isMobileDevice, + + // Client is a desktop Chrome, see #checkCapabilities() + isChrome, + + // Throttles mouse wheel navigation + lastMouseWheelStep = 0, + + // Delays updates to the URL due to a Chrome thumbnailer bug + writeURLTimeout = 0, + + // Flags if the interaction event listeners are bound + eventsAreBound = false, + + // The current auto-slide duration + autoSlide = 0, + + // Auto slide properties + autoSlidePlayer, + autoSlideTimeout = 0, + autoSlideStartTime = -1, + autoSlidePaused = false, + + // Holds information about the currently ongoing touch input + touch = { + startX: 0, + startY: 0, + startSpan: 0, + startCount: 0, + captured: false, + threshold: 40 + }, + + // Holds information about the keyboard shortcuts + keyboardShortcuts = { + 'N , SPACE': 'Next slide', + 'P': 'Previous slide', + '← , H': 'Navigate left', + '→ , L': 'Navigate right', + '↑ , K': 'Navigate up', + '↓ , J': 'Navigate down', + 'Home': 'First slide', + 'End': 'Last slide', + 'B , .': 'Pause', + 'F': 'Fullscreen', + 'ESC, O': 'Slide overview' + }; + + /** + * Starts up the presentation if the client is capable. + */ + function initialize( options ) { + + // Make sure we only initialize once + if( initialized === true ) return; + + initialized = true; + + checkCapabilities(); + + if( !features.transforms2d && !features.transforms3d ) { + document.body.setAttribute( 'class', 'no-transforms' ); + + // Since JS won't be running any further, we load all lazy + // loading elements upfront + var images = toArray( document.getElementsByTagName( 'img' ) ), + iframes = toArray( document.getElementsByTagName( 'iframe' ) ); + + var lazyLoadable = images.concat( iframes ); + + for( var i = 0, len = lazyLoadable.length; i < len; i++ ) { + var element = lazyLoadable[i]; + if( element.getAttribute( 'data-src' ) ) { + element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.removeAttribute( 'data-src' ); + } + } + + // If the browser doesn't support core features we won't be + // using JavaScript to control the presentation + return; + } + + // Cache references to key DOM elements + dom.wrapper = document.querySelector( '.reveal' ); + dom.slides = document.querySelector( '.reveal .slides' ); + + // Force a layout when the whole page, incl fonts, has loaded + window.addEventListener( 'load', layout, false ); + + var query = Reveal.getQueryHash(); + + // Do not accept new dependencies via query config to avoid + // the potential of malicious script injection + if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies']; + + // Copy options over to our config object + extend( config, options ); + extend( config, query ); + + // Hide the address bar in mobile browsers + hideAddressBar(); + + // Loads the dependencies and continues to #start() once done + load(); + + } + + /** + * Inspect the client to see what it's capable of, this + * should only happens once per runtime. + */ + function checkCapabilities() { + + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ); + isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); + + var testElement = document.createElement( 'div' ); + + features.transforms3d = 'WebkitPerspective' in testElement.style || + 'MozPerspective' in testElement.style || + 'msPerspective' in testElement.style || + 'OPerspective' in testElement.style || + 'perspective' in testElement.style; + + features.transforms2d = 'WebkitTransform' in testElement.style || + 'MozTransform' in testElement.style || + 'msTransform' in testElement.style || + 'OTransform' in testElement.style || + 'transform' in testElement.style; + + features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; + features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function'; + + features.canvas = !!document.createElement( 'canvas' ).getContext; + + // Transitions in the overview are disabled in desktop and + // Safari due to lag + features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA ); + + // Flags if we should use zoom instead of transform to scale + // up slides. Zoom produces crisper results but has a lot of + // xbrowser quirks so we only use it in whitelsited browsers. + features.zoom = 'zoom' in testElement.style && !isMobileDevice && + ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); + + } + + /** + * Loads the dependencies of reveal.js. Dependencies are + * defined via the configuration option 'dependencies' + * and will be loaded prior to starting/binding reveal.js. + * Some dependencies may have an 'async' flag, if so they + * will load after reveal.js has been started up. + */ + function load() { + + var scripts = [], + scriptsAsync = [], + scriptsToPreload = 0; + + // Called once synchronous scripts finish loading + function proceed() { + if( scriptsAsync.length ) { + // Load asynchronous scripts + head.js.apply( null, scriptsAsync ); + } + + start(); + } + + function loadScript( s ) { + head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], function() { + // Extension may contain callback functions + if( typeof s.callback === 'function' ) { + s.callback.apply( this ); + } + + if( --scriptsToPreload === 0 ) { + proceed(); + } + }); + } + + for( var i = 0, len = config.dependencies.length; i < len; i++ ) { + var s = config.dependencies[i]; + + // Load if there's no condition or the condition is truthy + if( !s.condition || s.condition() ) { + if( s.async ) { + scriptsAsync.push( s.src ); + } + else { + scripts.push( s.src ); + } + + loadScript( s ); + } + } + + if( scripts.length ) { + scriptsToPreload = scripts.length; + + // Load synchronous scripts + head.js.apply( null, scripts ); + } + else { + proceed(); + } + + } + + /** + * Starts up reveal.js by binding input events and navigating + * to the current URL deeplink if there is one. + */ + function start() { + + loaded = true; + + // Make sure we've got all the DOM elements we need + setupDOM(); + + // Listen to messages posted to this window + setupPostMessage(); + + // Prevent the slides from being scrolled out of view + setupScrollPrevention(); + + // Resets all vertical slides so that only the first is visible + resetVerticalSlides(); + + // Updates the presentation to match the current configuration values + configure(); + + // Read the initial hash + readURL(); + + // Update all backgrounds + updateBackground( true ); + + // Notify listeners that the presentation is ready but use a 1ms + // timeout to ensure it's not fired synchronously after #initialize() + setTimeout( function() { + // Enable transitions now that we're loaded + dom.slides.classList.remove( 'no-transition' ); + + dom.wrapper.classList.add( 'ready' ); + + dispatchEvent( 'ready', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + }, 1 ); + + // Special setup and config is required when printing to PDF + if( isPrintingPDF() ) { + removeEventListeners(); + + // The document needs to have loaded for the PDF layout + // measurements to be accurate + if( document.readyState === 'complete' ) { + setupPDF(); + } + else { + window.addEventListener( 'load', setupPDF ); + } + } + + } + + /** + * Finds and stores references to DOM elements which are + * required by the presentation. If a required element is + * not found, it is created. + */ + function setupDOM() { + + // Prevent transitions while we're loading + dom.slides.classList.add( 'no-transition' ); + + if( isMobileDevice ) { + dom.wrapper.classList.add( 'no-hover' ); + } + else { + dom.wrapper.classList.remove( 'no-hover' ); + } + + if( /iphone/gi.test( UA ) ) { + dom.wrapper.classList.add( 'ua-iphone' ); + } + else { + dom.wrapper.classList.remove( 'ua-iphone' ); + } + + // Background element + dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); + + // Progress bar + dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '' ); + dom.progressbar = dom.progress.querySelector( 'span' ); + + // Arrow controls + dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls', + '' + + '' + + '' + + '' ); + + // Slide number + dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' ); + + // Element containing notes that are visible to the audience + dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null ); + dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' ); + dom.speakerNotes.setAttribute( 'tabindex', '0' ); + + // Overlay graphic which is displayed during the paused mode + createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null ); + + dom.wrapper.setAttribute( 'role', 'application' ); + + // There can be multiple instances of controls throughout the page + dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) ); + dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) ); + dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) ); + dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) ); + dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); + dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); + + // The right and down arrows in the standard reveal.js controls + dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' ); + dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' ); + + dom.statusDiv = createStatusDiv(); + } + + /** + * Creates a hidden div with role aria-live to announce the + * current slide content. Hide the div off-screen to make it + * available only to Assistive Technologies. + * + * @return {HTMLElement} + */ + function createStatusDiv() { + + var statusDiv = document.getElementById( 'aria-status-div' ); + if( !statusDiv ) { + statusDiv = document.createElement( 'div' ); + statusDiv.style.position = 'absolute'; + statusDiv.style.height = '1px'; + statusDiv.style.width = '1px'; + statusDiv.style.overflow = 'hidden'; + statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )'; + statusDiv.setAttribute( 'id', 'aria-status-div' ); + statusDiv.setAttribute( 'aria-live', 'polite' ); + statusDiv.setAttribute( 'aria-atomic','true' ); + dom.wrapper.appendChild( statusDiv ); + } + return statusDiv; + + } + + /** + * Converts the given HTML element into a string of text + * that can be announced to a screen reader. Hidden + * elements are excluded. + */ + function getStatusText( node ) { + + var text = ''; + + // Text node + if( node.nodeType === 3 ) { + text += node.textContent; + } + // Element node + else if( node.nodeType === 1 ) { + + var isAriaHidden = node.getAttribute( 'aria-hidden' ); + var isDisplayHidden = window.getComputedStyle( node )['display'] === 'none'; + if( isAriaHidden !== 'true' && !isDisplayHidden ) { + + toArray( node.childNodes ).forEach( function( child ) { + text += getStatusText( child ); + } ); + + } + + } + + return text; + + } + + /** + * Configures the presentation for printing to a static + * PDF. + */ + function setupPDF() { + + var slideSize = getComputedSlideSize( window.innerWidth, window.innerHeight ); + + // Dimensions of the PDF pages + var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), + pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); + + // Dimensions of slides within the pages + var slideWidth = slideSize.width, + slideHeight = slideSize.height; + + // Let the browser know what page size we want to print + injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' ); + + // Limit the size of certain elements to the dimensions of the slide + injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); + + document.body.classList.add( 'print-pdf' ); + document.body.style.width = pageWidth + 'px'; + document.body.style.height = pageHeight + 'px'; + + // Make sure stretch elements fit on slide + layoutSlideContents( slideWidth, slideHeight ); + + // Add each slide's index as attributes on itself, we need these + // indices to generate slide numbers below + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { + hslide.setAttribute( 'data-index-h', h ); + + if( hslide.classList.contains( 'stack' ) ) { + toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { + vslide.setAttribute( 'data-index-h', h ); + vslide.setAttribute( 'data-index-v', v ); + } ); + } + } ); + + // Slide and slide background layout + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) === false ) { + // Center the slide inside of the page, giving the slide some margin + var left = ( pageWidth - slideWidth ) / 2, + top = ( pageHeight - slideHeight ) / 2; + + var contentHeight = slide.scrollHeight; + var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + + // Center slides vertically + if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { + top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); + } + + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + var page = document.createElement( 'div' ); + page.className = 'pdf-page'; + page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; + slide.parentNode.insertBefore( page, slide ); + page.appendChild( slide ); + + // Position the slide inside of the page + slide.style.left = left + 'px'; + slide.style.top = top + 'px'; + slide.style.width = slideWidth + 'px'; + + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); + } + + // Inject notes if `showNotes` is enabled + if( config.showNotes ) { + + // Are there notes for this slide? + var notes = getSlideNotes( slide ); + if( notes ) { + + var notesSpacing = 8; + var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; + var notesElement = document.createElement( 'div' ); + notesElement.classList.add( 'speaker-notes' ); + notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); + notesElement.innerHTML = notes; + + if( notesLayout === 'separate-page' ) { + page.parentNode.insertBefore( notesElement, page.nextSibling ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + + } + + } + + // Inject slide numbers if `slideNumbers` are enabled + if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) { + var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1, + slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1; + + var numberElement = document.createElement( 'div' ); + numberElement.classList.add( 'slide-number' ); + numberElement.classList.add( 'slide-number-pdf' ); + numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV ); + page.appendChild( numberElement ); + } + } + + } ); + + // Show all fragments + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' .fragment' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + } ); + + // Notify subscribers that the PDF layout is good to go + dispatchEvent( 'pdf-ready' ); + + } + + /** + * This is an unfortunate necessity. Some actions – such as + * an input field being focused in an iframe or using the + * keyboard to expand text selection beyond the bounds of + * a slide – can trigger our content to be pushed out of view. + * This scrolling can not be prevented by hiding overflow in + * CSS (we already do) so we have to resort to repeatedly + * checking if the slides have been offset :( + */ + function setupScrollPrevention() { + + setInterval( function() { + if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { + dom.wrapper.scrollTop = 0; + dom.wrapper.scrollLeft = 0; + } + }, 1000 ); + + } + + /** + * Creates an HTML element and returns a reference to it. + * If the element already exists the existing instance will + * be returned. + * + * @param {HTMLElement} container + * @param {string} tagname + * @param {string} classname + * @param {string} innerHTML + * + * @return {HTMLElement} + */ + function createSingletonNode( container, tagname, classname, innerHTML ) { + + // Find all nodes matching the description + var nodes = container.querySelectorAll( '.' + classname ); + + // Check all matches to find one which is a direct child of + // the specified container + for( var i = 0; i < nodes.length; i++ ) { + var testNode = nodes[i]; + if( testNode.parentNode === container ) { + return testNode; + } + } + + // If no node was found, create it now + var node = document.createElement( tagname ); + node.className = classname; + if( typeof innerHTML === 'string' ) { + node.innerHTML = innerHTML; + } + container.appendChild( node ); + + return node; + + } + + /** + * Creates the slide background elements and appends them + * to the background container. One element is created per + * slide no matter if the given slide has visible background. + */ + function createBackgrounds() { + + var printMode = isPrintingPDF(); + + // Clear prior backgrounds + dom.background.innerHTML = ''; + dom.background.classList.add( 'no-transition' ); + + // Iterate over all horizontal slides + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { + + var backgroundStack = createBackground( slideh, dom.background ); + + // Iterate over all vertical slides + toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { + + createBackground( slidev, backgroundStack ); + + backgroundStack.classList.add( 'stack' ); + + } ); + + } ); + + // Add parallax background if specified + if( config.parallaxBackgroundImage ) { + + dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")'; + dom.background.style.backgroundSize = config.parallaxBackgroundSize; + + // Make sure the below properties are set on the element - these properties are + // needed for proper transitions to be set on the element via CSS. To remove + // annoying background slide-in effect when the presentation starts, apply + // these properties after short time delay + setTimeout( function() { + dom.wrapper.classList.add( 'has-parallax-background' ); + }, 1 ); + + } + else { + + dom.background.style.backgroundImage = ''; + dom.wrapper.classList.remove( 'has-parallax-background' ); + + } + + } + + /** + * Creates a background for the given slide. + * + * @param {HTMLElement} slide + * @param {HTMLElement} container The element that the background + * should be appended to + * @return {HTMLElement} New background div + */ + function createBackground( slide, container ) { + + var data = { + background: slide.getAttribute( 'data-background' ), + backgroundSize: slide.getAttribute( 'data-background-size' ), + backgroundImage: slide.getAttribute( 'data-background-image' ), + backgroundVideo: slide.getAttribute( 'data-background-video' ), + backgroundIframe: slide.getAttribute( 'data-background-iframe' ), + backgroundColor: slide.getAttribute( 'data-background-color' ), + backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), + backgroundPosition: slide.getAttribute( 'data-background-position' ), + backgroundTransition: slide.getAttribute( 'data-background-transition' ) + }; + + var element = document.createElement( 'div' ); + + // Carry over custom classes from the slide to the background + element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + + if( data.background ) { + // Auto-wrap image urls in url(...) + if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#]|$)/gi.test( data.background ) ) { + slide.setAttribute( 'data-background-image', data.background ); + } + else { + element.style.background = data.background; + } + } + + // Create a hash for this combination of background settings. + // This is used to determine when two slide backgrounds are + // the same. + if( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) { + element.setAttribute( 'data-background-hash', data.background + + data.backgroundSize + + data.backgroundImage + + data.backgroundVideo + + data.backgroundIframe + + data.backgroundColor + + data.backgroundRepeat + + data.backgroundPosition + + data.backgroundTransition ); + } + + // Additional and optional background properties + if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize; + if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); + if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; + if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); + + container.appendChild( element ); + + // If backgrounds are being recreated, clear old classes + slide.classList.remove( 'has-dark-background' ); + slide.classList.remove( 'has-light-background' ); + + slide.slideBackgroundElement = element; + + // If this slide has a background color, add a class that + // signals if it is light or dark. If the slide has no background + // color, no class will be set + var computedBackgroundStyle = window.getComputedStyle( element ); + if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) { + var rgb = colorToRgb( computedBackgroundStyle.backgroundColor ); + + // Ignore fully transparent backgrounds. Some browsers return + // rgba(0,0,0,0) when reading the computed background color of + // an element with no background + if( rgb && rgb.a !== 0 ) { + if( colorBrightness( computedBackgroundStyle.backgroundColor ) < 128 ) { + slide.classList.add( 'has-dark-background' ); + } + else { + slide.classList.add( 'has-light-background' ); + } + } + } + + return element; + + } + + /** + * Registers a listener to postMessage events, this makes it + * possible to call all reveal.js API methods from another + * window. For example: + * + * revealWindow.postMessage( JSON.stringify({ + * method: 'slide', + * args: [ 2 ] + * }), '*' ); + */ + function setupPostMessage() { + + if( config.postMessage ) { + window.addEventListener( 'message', function ( event ) { + var data = event.data; + + // Make sure we're dealing with JSON + if( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) { + data = JSON.parse( data ); + + // Check if the requested method can be found + if( data.method && typeof Reveal[data.method] === 'function' ) { + Reveal[data.method].apply( Reveal, data.args ); + } + } + }, false ); + } + + } + + /** + * Applies the configuration settings from the config + * object. May be called multiple times. + * + * @param {object} options + */ + function configure( options ) { + + var oldTransition = config.transition; + + // New config options may be passed when this method + // is invoked through the API after initialization + if( typeof options === 'object' ) extend( config, options ); + + // Abort if reveal.js hasn't finished loading, config + // changes will be applied automatically once loading + // finishes + if( loaded === false ) return; + + var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; + + // Remove the previously configured transition class + dom.wrapper.classList.remove( oldTransition ); + + // Force linear transition based on browser capabilities + if( features.transforms3d === false ) config.transition = 'linear'; + + dom.wrapper.classList.add( config.transition ); + + dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed ); + dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition ); + + dom.controls.style.display = config.controls ? 'block' : 'none'; + dom.progress.style.display = config.progress ? 'block' : 'none'; + + dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout ); + dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows ); + + if( config.shuffle ) { + shuffle(); + } + + if( config.rtl ) { + dom.wrapper.classList.add( 'rtl' ); + } + else { + dom.wrapper.classList.remove( 'rtl' ); + } + + if( config.center ) { + dom.wrapper.classList.add( 'center' ); + } + else { + dom.wrapper.classList.remove( 'center' ); + } + + // Exit the paused mode if it was configured off + if( config.pause === false ) { + resume(); + } + + if( config.showNotes ) { + dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' ); + } + + if( config.mouseWheel ) { + document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF + document.addEventListener( 'mousewheel', onDocumentMouseScroll, false ); + } + else { + document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF + document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false ); + } + + // Rolling 3D links + if( config.rollingLinks ) { + enableRollingLinks(); + } + else { + disableRollingLinks(); + } + + // Iframe link previews + if( config.previewLinks ) { + enablePreviewLinks(); + disablePreviewLinks( '[data-preview-link=false]' ); + } + else { + disablePreviewLinks(); + enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' ); + } + + // Remove existing auto-slide controls + if( autoSlidePlayer ) { + autoSlidePlayer.destroy(); + autoSlidePlayer = null; + } + + // Generate auto-slide controls if needed + if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) { + autoSlidePlayer = new Playback( dom.wrapper, function() { + return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 ); + } ); + + autoSlidePlayer.on( 'click', onAutoSlidePlayerClick ); + autoSlidePaused = false; + } + + // When fragments are turned off they should be visible + if( config.fragments === false ) { + toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) { + element.classList.add( 'visible' ); + element.classList.remove( 'current-fragment' ); + } ); + } + + // Slide numbers + var slideNumberDisplay = 'none'; + if( config.slideNumber && !isPrintingPDF() ) { + if( config.showSlideNumber === 'all' ) { + slideNumberDisplay = 'block'; + } + else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) { + slideNumberDisplay = 'block'; + } + } + + dom.slideNumber.style.display = slideNumberDisplay; + + sync(); + + } + + /** + * Binds all event listeners. + */ + function addEventListeners() { + + eventsAreBound = true; + + window.addEventListener( 'hashchange', onWindowHashChange, false ); + window.addEventListener( 'resize', onWindowResize, false ); + + if( config.touch ) { + dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); + + // Support pointer-style touch interaction as well + if( window.navigator.pointerEnabled ) { + // IE 11 uses un-prefixed version of pointer events + dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.addEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.addEventListener( 'pointerup', onPointerUp, false ); + } + else if( window.navigator.msPointerEnabled ) { + // IE 10 uses prefixed version of pointer events + dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false ); + } + } + + if( config.keyboard ) { + document.addEventListener( 'keydown', onDocumentKeyDown, false ); + document.addEventListener( 'keypress', onDocumentKeyPress, false ); + } + + if( config.progress && dom.progress ) { + dom.progress.addEventListener( 'click', onProgressClicked, false ); + } + + if( config.focusBodyOnPageVisibilityChange ) { + var visibilityChange; + + if( 'hidden' in document ) { + visibilityChange = 'visibilitychange'; + } + else if( 'msHidden' in document ) { + visibilityChange = 'msvisibilitychange'; + } + else if( 'webkitHidden' in document ) { + visibilityChange = 'webkitvisibilitychange'; + } + + if( visibilityChange ) { + document.addEventListener( visibilityChange, onPageVisibilityChange, false ); + } + } + + // Listen to both touch and click events, in case the device + // supports both + var pointerEvents = [ 'touchstart', 'click' ]; + + // Only support touch for Android, fixes double navigations in + // stock browser + if( UA.match( /android/gi ) ) { + pointerEvents = [ 'touchstart' ]; + } + + pointerEvents.forEach( function( eventName ) { + dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } ); + } ); + + } + + /** + * Unbinds all event listeners. + */ + function removeEventListeners() { + + eventsAreBound = false; + + document.removeEventListener( 'keydown', onDocumentKeyDown, false ); + document.removeEventListener( 'keypress', onDocumentKeyPress, false ); + window.removeEventListener( 'hashchange', onWindowHashChange, false ); + window.removeEventListener( 'resize', onWindowResize, false ); + + dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false ); + + // IE11 + if( window.navigator.pointerEnabled ) { + dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); + } + // IE10 + else if( window.navigator.msPointerEnabled ) { + dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); + } + + if ( config.progress && dom.progress ) { + dom.progress.removeEventListener( 'click', onProgressClicked, false ); + } + + [ 'touchstart', 'click' ].forEach( function( eventName ) { + dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } ); + } ); + + } + + /** + * Extend object a with the properties of object b. + * If there's a conflict, object b takes precedence. + * + * @param {object} a + * @param {object} b + */ + function extend( a, b ) { + + for( var i in b ) { + a[ i ] = b[ i ]; + } + + return a; + + } + + /** + * Converts the target object to an array. + * + * @param {object} o + * @return {object[]} + */ + function toArray( o ) { + + return Array.prototype.slice.call( o ); + + } + + /** + * Utility for deserializing a value. + * + * @param {*} value + * @return {*} + */ + function deserialize( value ) { + + if( typeof value === 'string' ) { + if( value === 'null' ) return null; + else if( value === 'true' ) return true; + else if( value === 'false' ) return false; + else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value ); + } + + return value; + + } + + /** + * Measures the distance in pixels between point a + * and point b. + * + * @param {object} a point with x/y properties + * @param {object} b point with x/y properties + * + * @return {number} + */ + function distanceBetween( a, b ) { + + var dx = a.x - b.x, + dy = a.y - b.y; + + return Math.sqrt( dx*dx + dy*dy ); + + } + + /** + * Applies a CSS transform to the target element. + * + * @param {HTMLElement} element + * @param {string} transform + */ + function transformElement( element, transform ) { + + element.style.WebkitTransform = transform; + element.style.MozTransform = transform; + element.style.msTransform = transform; + element.style.transform = transform; + + } + + /** + * Applies CSS transforms to the slides container. The container + * is transformed from two separate sources: layout and the overview + * mode. + * + * @param {object} transforms + */ + function transformSlides( transforms ) { + + // Pick up new transforms from arguments + if( typeof transforms.layout === 'string' ) slidesTransform.layout = transforms.layout; + if( typeof transforms.overview === 'string' ) slidesTransform.overview = transforms.overview; + + // Apply the transforms to the slides container + if( slidesTransform.layout ) { + transformElement( dom.slides, slidesTransform.layout + ' ' + slidesTransform.overview ); + } + else { + transformElement( dom.slides, slidesTransform.overview ); + } + + } + + /** + * Injects the given CSS styles into the DOM. + * + * @param {string} value + */ + function injectStyleSheet( value ) { + + var tag = document.createElement( 'style' ); + tag.type = 'text/css'; + if( tag.styleSheet ) { + tag.styleSheet.cssText = value; + } + else { + tag.appendChild( document.createTextNode( value ) ); + } + document.getElementsByTagName( 'head' )[0].appendChild( tag ); + + } + + /** + * Find the closest parent that matches the given + * selector. + * + * @param {HTMLElement} target The child element + * @param {String} selector The CSS selector to match + * the parents against + * + * @return {HTMLElement} The matched parent or null + * if no matching parent was found + */ + function closestParent( target, selector ) { + + var parent = target.parentNode; + + while( parent ) { + + // There's some overhead doing this each time, we don't + // want to rewrite the element prototype but should still + // be enough to feature detect once at startup... + var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector; + + // If we find a match, we're all set + if( matchesMethod && matchesMethod.call( parent, selector ) ) { + return parent; + } + + // Keep searching + parent = parent.parentNode; + + } + + return null; + + } + + /** + * Converts various color input formats to an {r:0,g:0,b:0} object. + * + * @param {string} color The string representation of a color + * @example + * colorToRgb('#000'); + * @example + * colorToRgb('#000000'); + * @example + * colorToRgb('rgb(0,0,0)'); + * @example + * colorToRgb('rgba(0,0,0)'); + * + * @return {{r: number, g: number, b: number, [a]: number}|null} + */ + function colorToRgb( color ) { + + var hex3 = color.match( /^#([0-9a-f]{3})$/i ); + if( hex3 && hex3[1] ) { + hex3 = hex3[1]; + return { + r: parseInt( hex3.charAt( 0 ), 16 ) * 0x11, + g: parseInt( hex3.charAt( 1 ), 16 ) * 0x11, + b: parseInt( hex3.charAt( 2 ), 16 ) * 0x11 + }; + } + + var hex6 = color.match( /^#([0-9a-f]{6})$/i ); + if( hex6 && hex6[1] ) { + hex6 = hex6[1]; + return { + r: parseInt( hex6.substr( 0, 2 ), 16 ), + g: parseInt( hex6.substr( 2, 2 ), 16 ), + b: parseInt( hex6.substr( 4, 2 ), 16 ) + }; + } + + var rgb = color.match( /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i ); + if( rgb ) { + return { + r: parseInt( rgb[1], 10 ), + g: parseInt( rgb[2], 10 ), + b: parseInt( rgb[3], 10 ) + }; + } + + var rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i ); + if( rgba ) { + return { + r: parseInt( rgba[1], 10 ), + g: parseInt( rgba[2], 10 ), + b: parseInt( rgba[3], 10 ), + a: parseFloat( rgba[4] ) + }; + } + + return null; + + } + + /** + * Calculates brightness on a scale of 0-255. + * + * @param {string} color See colorToRgb for supported formats. + * @see {@link colorToRgb} + */ + function colorBrightness( color ) { + + if( typeof color === 'string' ) color = colorToRgb( color ); + + if( color ) { + return ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000; + } + + return null; + + } + + /** + * Returns the remaining height within the parent of the + * target element. + * + * remaining height = [ configured parent height ] - [ current parent height ] + * + * @param {HTMLElement} element + * @param {number} [height] + */ + function getRemainingHeight( element, height ) { + + height = height || 0; + + if( element ) { + var newHeight, oldHeight = element.style.height; + + // Change the .stretch element height to 0 in order find the height of all + // the other elements + element.style.height = '0px'; + newHeight = height - element.parentNode.offsetHeight; + + // Restore the old height, just in case + element.style.height = oldHeight + 'px'; + + return newHeight; + } + + return height; + + } + + /** + * Checks if this instance is being used to print a PDF. + */ + function isPrintingPDF() { + + return ( /print-pdf/gi ).test( window.location.search ); + + } + + /** + * Hides the address bar if we're on a mobile device. + */ + function hideAddressBar() { + + if( config.hideAddressBar && isMobileDevice ) { + // Events that should trigger the address bar to hide + window.addEventListener( 'load', removeAddressBar, false ); + window.addEventListener( 'orientationchange', removeAddressBar, false ); + } + + } + + /** + * Causes the address bar to hide on mobile devices, + * more vertical space ftw. + */ + function removeAddressBar() { + + setTimeout( function() { + window.scrollTo( 0, 1 ); + }, 10 ); + + } + + /** + * Dispatches an event of the specified type from the + * reveal DOM element. + */ + function dispatchEvent( type, args ) { + + var event = document.createEvent( 'HTMLEvents', 1, 2 ); + event.initEvent( type, true, true ); + extend( event, args ); + dom.wrapper.dispatchEvent( event ); + + // If we're in an iframe, post each reveal.js event to the + // parent window. Used by the notes plugin + if( config.postMessageEvents && window.parent !== window.self ) { + window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' ); + } + + } + + /** + * Wrap all links in 3D goodness. + */ + function enableRollingLinks() { + + if( features.transforms3d && !( 'msPerspective' in document.body.style ) ) { + var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + + if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) { + var span = document.createElement('span'); + span.setAttribute('data-title', anchor.text); + span.innerHTML = anchor.innerHTML; + + anchor.classList.add( 'roll' ); + anchor.innerHTML = ''; + anchor.appendChild(span); + } + } + } + + } + + /** + * Unwrap all 3D links. + */ + function disableRollingLinks() { + + var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a.roll' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + var span = anchor.querySelector( 'span' ); + + if( span ) { + anchor.classList.remove( 'roll' ); + anchor.innerHTML = span.innerHTML; + } + } + + } + + /** + * Bind preview frame links. + * + * @param {string} [selector=a] - selector for anchors + */ + function enablePreviewLinks( selector ) { + + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); + + anchors.forEach( function( element ) { + if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { + element.addEventListener( 'click', onPreviewLinkClicked, false ); + } + } ); + + } + + /** + * Unbind preview frame links. + */ + function disablePreviewLinks( selector ) { + + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); + + anchors.forEach( function( element ) { + if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { + element.removeEventListener( 'click', onPreviewLinkClicked, false ); + } + } ); + + } + + /** + * Opens a preview window for the target URL. + * + * @param {string} url - url for preview iframe src + */ + function showPreview( url ) { + + closeOverlay(); + + dom.overlay = document.createElement( 'div' ); + dom.overlay.classList.add( 'overlay' ); + dom.overlay.classList.add( 'overlay-preview' ); + dom.wrapper.appendChild( dom.overlay ); + + dom.overlay.innerHTML = [ + '
', + '', + '', + '
', + '
', + '
', + '', + '', + 'Unable to load iframe. This is likely due to the site\'s policy (x-frame-options).', + '', + '
' + ].join(''); + + dom.overlay.querySelector( 'iframe' ).addEventListener( 'load', function( event ) { + dom.overlay.classList.add( 'loaded' ); + }, false ); + + dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) { + closeOverlay(); + event.preventDefault(); + }, false ); + + dom.overlay.querySelector( '.external' ).addEventListener( 'click', function( event ) { + closeOverlay(); + }, false ); + + setTimeout( function() { + dom.overlay.classList.add( 'visible' ); + }, 1 ); + + } + + /** + * Open or close help overlay window. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * help is open, false means it's closed. + */ + function toggleHelp( override ){ + + if( typeof override === 'boolean' ) { + override ? showHelp() : closeOverlay(); + } + else { + if( dom.overlay ) { + closeOverlay(); + } + else { + showHelp(); + } + } + } + + /** + * Opens an overlay window with help material. + */ + function showHelp() { + + if( config.help ) { + + closeOverlay(); + + dom.overlay = document.createElement( 'div' ); + dom.overlay.classList.add( 'overlay' ); + dom.overlay.classList.add( 'overlay-help' ); + dom.wrapper.appendChild( dom.overlay ); + + var html = '

Keyboard Shortcuts


'; + + html += ''; + for( var key in keyboardShortcuts ) { + html += ''; + } + + html += '
KEYACTION
' + key + '' + keyboardShortcuts[ key ] + '
'; + + dom.overlay.innerHTML = [ + '
', + '', + '
', + '
', + '
'+ html +'
', + '
' + ].join(''); + + dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) { + closeOverlay(); + event.preventDefault(); + }, false ); + + setTimeout( function() { + dom.overlay.classList.add( 'visible' ); + }, 1 ); + + } + + } + + /** + * Closes any currently open overlay. + */ + function closeOverlay() { + + if( dom.overlay ) { + dom.overlay.parentNode.removeChild( dom.overlay ); + dom.overlay = null; + } + + } + + /** + * Applies JavaScript-controlled layout rules to the + * presentation. + */ + function layout() { + + if( dom.wrapper && !isPrintingPDF() ) { + + var size = getComputedSlideSize(); + + // Layout the contents of the slides + layoutSlideContents( config.width, config.height ); + + dom.slides.style.width = size.width + 'px'; + dom.slides.style.height = size.height + 'px'; + + // Determine scale of content to fit within available space + scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); + + // Respect max/min scale settings + scale = Math.max( scale, config.minScale ); + scale = Math.min( scale, config.maxScale ); + + // Don't apply any scaling styles if scale is 1 + if( scale === 1 ) { + dom.slides.style.zoom = ''; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + else { + // Prefer zoom for scaling up so that content remains crisp. + // Don't use zoom to scale down since that can lead to shifts + // in text layout/line breaks. + if( scale > 1 && features.zoom ) { + dom.slides.style.zoom = scale; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + // Apply scale transform as a fallback + else { + dom.slides.style.zoom = ''; + dom.slides.style.left = '50%'; + dom.slides.style.top = '50%'; + dom.slides.style.bottom = 'auto'; + dom.slides.style.right = 'auto'; + transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); + } + } + + // Select all slides, vertical and horizontal + var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); + + for( var i = 0, len = slides.length; i < len; i++ ) { + var slide = slides[ i ]; + + // Don't bother updating invisible slides + if( slide.style.display === 'none' ) { + continue; + } + + if( config.center || slide.classList.contains( 'center' ) ) { + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) ) { + slide.style.top = 0; + } + else { + slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px'; + } + } + else { + slide.style.top = ''; + } + + } + + updateProgress(); + updateParallax(); + + if( isOverview() ) { + updateOverview(); + } + + } + + } + + /** + * Applies layout logic to the contents of all slides in + * the presentation. + * + * @param {string|number} width + * @param {string|number} height + */ + function layoutSlideContents( width, height ) { + + // Handle sizing of elements with the 'stretch' class + toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { + + // Determine how much vertical space we can use + var remainingHeight = getRemainingHeight( element, height ); + + // Consider the aspect ratio of media elements + if( /(img|video)/gi.test( element.nodeName ) ) { + var nw = element.naturalWidth || element.videoWidth, + nh = element.naturalHeight || element.videoHeight; + + var es = Math.min( width / nw, remainingHeight / nh ); + + element.style.width = ( nw * es ) + 'px'; + element.style.height = ( nh * es ) + 'px'; + + } + else { + element.style.width = width + 'px'; + element.style.height = remainingHeight + 'px'; + } + + } ); + + } + + /** + * Calculates the computed pixel size of our slides. These + * values are based on the width and height configuration + * options. + * + * @param {number} [presentationWidth=dom.wrapper.offsetWidth] + * @param {number} [presentationHeight=dom.wrapper.offsetHeight] + */ + function getComputedSlideSize( presentationWidth, presentationHeight ) { + + var size = { + // Slide size + width: config.width, + height: config.height, + + // Presentation size + presentationWidth: presentationWidth || dom.wrapper.offsetWidth, + presentationHeight: presentationHeight || dom.wrapper.offsetHeight + }; + + // Reduce available space by margin + size.presentationWidth -= ( size.presentationWidth * config.margin ); + size.presentationHeight -= ( size.presentationHeight * config.margin ); + + // Slide width may be a percentage of available width + if( typeof size.width === 'string' && /%$/.test( size.width ) ) { + size.width = parseInt( size.width, 10 ) / 100 * size.presentationWidth; + } + + // Slide height may be a percentage of available height + if( typeof size.height === 'string' && /%$/.test( size.height ) ) { + size.height = parseInt( size.height, 10 ) / 100 * size.presentationHeight; + } + + return size; + + } + + /** + * Stores the vertical index of a stack so that the same + * vertical slide can be selected when navigating to and + * from the stack. + * + * @param {HTMLElement} stack The vertical stack element + * @param {string|number} [v=0] Index to memorize + */ + function setPreviousVerticalIndex( stack, v ) { + + if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) { + stack.setAttribute( 'data-previous-indexv', v || 0 ); + } + + } + + /** + * Retrieves the vertical index which was stored using + * #setPreviousVerticalIndex() or 0 if no previous index + * exists. + * + * @param {HTMLElement} stack The vertical stack element + */ + function getPreviousVerticalIndex( stack ) { + + if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) { + // Prefer manually defined start-indexv + var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv'; + + return parseInt( stack.getAttribute( attributeName ) || 0, 10 ); + } + + return 0; + + } + + /** + * Displays the overview of slides (quick nav) by scaling + * down and arranging all slide elements. + */ + function activateOverview() { + + // Only proceed if enabled in config + if( config.overview && !isOverview() ) { + + overview = true; + + dom.wrapper.classList.add( 'overview' ); + dom.wrapper.classList.remove( 'overview-deactivating' ); + + if( features.overviewTransitions ) { + setTimeout( function() { + dom.wrapper.classList.add( 'overview-animated' ); + }, 1 ); + } + + // Don't auto-slide while in overview mode + cancelAutoSlide(); + + // Move the backgrounds element into the slide container to + // that the same scaling is applied + dom.slides.appendChild( dom.background ); + + // Clicking on an overview slide navigates to it + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + if( !slide.classList.contains( 'stack' ) ) { + slide.addEventListener( 'click', onOverviewSlideClicked, true ); + } + } ); + + // Calculate slide sizes + var margin = 70; + var slideSize = getComputedSlideSize(); + overviewSlideWidth = slideSize.width + margin; + overviewSlideHeight = slideSize.height + margin; + + // Reverse in RTL mode + if( config.rtl ) { + overviewSlideWidth = -overviewSlideWidth; + } + + updateSlidesVisibility(); + layoutOverview(); + updateOverview(); + + layout(); + + // Notify observers of the overview showing + dispatchEvent( 'overviewshown', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + + } + + } + + /** + * Uses CSS transforms to position all slides in a grid for + * display inside of the overview mode. + */ + function layoutOverview() { + + // Layout slides + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { + hslide.setAttribute( 'data-index-h', h ); + transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); + + if( hslide.classList.contains( 'stack' ) ) { + + toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { + vslide.setAttribute( 'data-index-h', h ); + vslide.setAttribute( 'data-index-v', v ); + + transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); + } ); + + } + } ); + + // Layout slide backgrounds + toArray( dom.background.childNodes ).forEach( function( hbackground, h ) { + transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); + + toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) { + transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); + } ); + } ); + + } + + /** + * Moves the overview viewport to the current slides. + * Called each time the current slide changes. + */ + function updateOverview() { + + var vmin = Math.min( window.innerWidth, window.innerHeight ); + var scale = Math.max( vmin / 5, 150 ) / vmin; + + transformSlides( { + overview: [ + 'scale('+ scale +')', + 'translateX('+ ( -indexh * overviewSlideWidth ) +'px)', + 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)' + ].join( ' ' ) + } ); + + } + + /** + * Exits the slide overview and enters the currently + * active slide. + */ + function deactivateOverview() { + + // Only proceed if enabled in config + if( config.overview ) { + + overview = false; + + dom.wrapper.classList.remove( 'overview' ); + dom.wrapper.classList.remove( 'overview-animated' ); + + // Temporarily add a class so that transitions can do different things + // depending on whether they are exiting/entering overview, or just + // moving from slide to slide + dom.wrapper.classList.add( 'overview-deactivating' ); + + setTimeout( function () { + dom.wrapper.classList.remove( 'overview-deactivating' ); + }, 1 ); + + // Move the background element back out + dom.wrapper.appendChild( dom.background ); + + // Clean up changes made to slides + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + transformElement( slide, '' ); + + slide.removeEventListener( 'click', onOverviewSlideClicked, true ); + } ); + + // Clean up changes made to backgrounds + toArray( dom.background.querySelectorAll( '.slide-background' ) ).forEach( function( background ) { + transformElement( background, '' ); + } ); + + transformSlides( { overview: '' } ); + + slide( indexh, indexv ); + + layout(); + + cueAutoSlide(); + + // Notify observers of the overview hiding + dispatchEvent( 'overviewhidden', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + + } + } + + /** + * Toggles the slide overview mode on and off. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * overview is open, false means it's closed. + */ + function toggleOverview( override ) { + + if( typeof override === 'boolean' ) { + override ? activateOverview() : deactivateOverview(); + } + else { + isOverview() ? deactivateOverview() : activateOverview(); + } + + } + + /** + * Checks if the overview is currently active. + * + * @return {Boolean} true if the overview is active, + * false otherwise + */ + function isOverview() { + + return overview; + + } + + /** + * Checks if the current or specified slide is vertical + * (nested within another slide). + * + * @param {HTMLElement} [slide=currentSlide] The slide to check + * orientation of + * @return {Boolean} + */ + function isVerticalSlide( slide ) { + + // Prefer slide argument, otherwise use current slide + slide = slide ? slide : currentSlide; + + return slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i ); + + } + + /** + * Handling the fullscreen functionality via the fullscreen API + * + * @see http://fullscreen.spec.whatwg.org/ + * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode + */ + function enterFullscreen() { + + var element = document.documentElement; + + // Check which implementation is available + var requestMethod = element.requestFullscreen || + element.webkitRequestFullscreen || + element.webkitRequestFullScreen || + element.mozRequestFullScreen || + element.msRequestFullscreen; + + if( requestMethod ) { + requestMethod.apply( element ); + } + + } + + /** + * Enters the paused mode which fades everything on screen to + * black. + */ + function pause() { + + if( config.pause ) { + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + + cancelAutoSlide(); + dom.wrapper.classList.add( 'paused' ); + + if( wasPaused === false ) { + dispatchEvent( 'paused' ); + } + } + + } + + /** + * Exits from the paused mode. + */ + function resume() { + + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + dom.wrapper.classList.remove( 'paused' ); + + cueAutoSlide(); + + if( wasPaused ) { + dispatchEvent( 'resumed' ); + } + + } + + /** + * Toggles the paused mode on and off. + */ + function togglePause( override ) { + + if( typeof override === 'boolean' ) { + override ? pause() : resume(); + } + else { + isPaused() ? resume() : pause(); + } + + } + + /** + * Checks if we are currently in the paused mode. + * + * @return {Boolean} + */ + function isPaused() { + + return dom.wrapper.classList.contains( 'paused' ); + + } + + /** + * Toggles the auto slide mode on and off. + * + * @param {Boolean} [override] Flag which sets the desired state. + * True means autoplay starts, false means it stops. + */ + + function toggleAutoSlide( override ) { + + if( typeof override === 'boolean' ) { + override ? resumeAutoSlide() : pauseAutoSlide(); + } + + else { + autoSlidePaused ? resumeAutoSlide() : pauseAutoSlide(); + } + + } + + /** + * Checks if the auto slide mode is currently on. + * + * @return {Boolean} + */ + function isAutoSliding() { + + return !!( autoSlide && !autoSlidePaused ); + + } + + /** + * Steps from the current point in the presentation to the + * slide which matches the specified horizontal and vertical + * indices. + * + * @param {number} [h=indexh] Horizontal index of the target slide + * @param {number} [v=indexv] Vertical index of the target slide + * @param {number} [f] Index of a fragment within the + * target slide to activate + * @param {number} [o] Origin for use in multimaster environments + */ + function slide( h, v, f, o ) { + + // Remember where we were at before + previousSlide = currentSlide; + + // Query all horizontal slides in the deck + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + + // Abort if there are no slides + if( horizontalSlides.length === 0 ) return; + + // If no vertical index is specified and the upcoming slide is a + // stack, resume at its previous vertical index + if( v === undefined && !isOverview() ) { + v = getPreviousVerticalIndex( horizontalSlides[ h ] ); + } + + // If we were on a vertical stack, remember what vertical index + // it was on so we can resume at the same position when returning + if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) { + setPreviousVerticalIndex( previousSlide.parentNode, indexv ); + } + + // Remember the state before this slide + var stateBefore = state.concat(); + + // Reset the state array + state.length = 0; + + var indexhBefore = indexh || 0, + indexvBefore = indexv || 0; + + // Activate and transition to the new slide + indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h ); + indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v ); + + // Update the visibility of slides now that the indices have changed + updateSlidesVisibility(); + + layout(); + + // Apply the new state + stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { + // Check if this state existed on the previous slide. If it + // did, we will avoid adding it repeatedly + for( var j = 0; j < stateBefore.length; j++ ) { + if( stateBefore[j] === state[i] ) { + stateBefore.splice( j, 1 ); + continue stateLoop; + } + } + + document.documentElement.classList.add( state[i] ); + + // Dispatch custom event matching the state's name + dispatchEvent( state[i] ); + } + + // Clean up the remains of the previous state + while( stateBefore.length ) { + document.documentElement.classList.remove( stateBefore.pop() ); + } + + // Update the overview if it's currently active + if( isOverview() ) { + updateOverview(); + } + + // Find the current horizontal slide and any possible vertical slides + // within it + var currentHorizontalSlide = horizontalSlides[ indexh ], + currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); + + // Store references to the previous and current slides + currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; + + // Show fragment, if specified + if( typeof f !== 'undefined' ) { + navigateFragment( f ); + } + + // Dispatch an event if the slide changed + var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); + if( slideChanged ) { + dispatchEvent( 'slidechanged', { + 'indexh': indexh, + 'indexv': indexv, + 'previousSlide': previousSlide, + 'currentSlide': currentSlide, + 'origin': o + } ); + } + else { + // Ensure that the previous slide is never the same as the current + previousSlide = null; + } + + // Solves an edge case where the previous slide maintains the + // 'present' class when navigating between adjacent vertical + // stacks + if( previousSlide ) { + previousSlide.classList.remove( 'present' ); + previousSlide.setAttribute( 'aria-hidden', 'true' ); + + // Reset all slides upon navigate to home + // Issue: #285 + if ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { + // Launch async task + setTimeout( function () { + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; + for( i in slides ) { + if( slides[i] ) { + // Reset stack + setPreviousVerticalIndex( slides[i], 0 ); + } + } + }, 0 ); + } + } + + // Handle embedded content + if( slideChanged || !previousSlide ) { + stopEmbeddedContent( previousSlide ); + startEmbeddedContent( currentSlide ); + } + + // Announce the current slide contents, for screen readers + dom.statusDiv.textContent = getStatusText( currentSlide ); + + updateControls(); + updateProgress(); + updateBackground(); + updateParallax(); + updateSlideNumber(); + updateNotes(); + + // Update the URL hash + writeURL(); + + cueAutoSlide(); + + } + + /** + * Syncs the presentation with the current DOM. Useful + * when new slides or control elements are added or when + * the configuration has changed. + */ + function sync() { + + // Subscribe to input + removeEventListeners(); + addEventListeners(); + + // Force a layout to make sure the current config is accounted for + layout(); + + // Reflect the current autoSlide value + autoSlide = config.autoSlide; + + // Start auto-sliding if it's enabled + cueAutoSlide(); + + // Re-create the slide backgrounds + createBackgrounds(); + + // Write the current hash to the URL + writeURL(); + + sortAllFragments(); + + updateControls(); + updateProgress(); + updateSlideNumber(); + updateSlidesVisibility(); + updateBackground( true ); + updateNotesVisibility(); + updateNotes(); + + formatEmbeddedContent(); + + // Start or stop embedded content depending on global config + if( config.autoPlayMedia === false ) { + stopEmbeddedContent( currentSlide, { unloadIframes: false } ); + } + else { + startEmbeddedContent( currentSlide ); + } + + if( isOverview() ) { + layoutOverview(); + } + + } + + /** + * Resets all vertical slides so that only the first + * is visible. + */ + function resetVerticalSlides() { + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + horizontalSlides.forEach( function( horizontalSlide ) { + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + verticalSlides.forEach( function( verticalSlide, y ) { + + if( y > 0 ) { + verticalSlide.classList.remove( 'present' ); + verticalSlide.classList.remove( 'past' ); + verticalSlide.classList.add( 'future' ); + verticalSlide.setAttribute( 'aria-hidden', 'true' ); + } + + } ); + + } ); + + } + + /** + * Sorts and formats all of fragments in the + * presentation. + */ + function sortAllFragments() { + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + horizontalSlides.forEach( function( horizontalSlide ) { + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + verticalSlides.forEach( function( verticalSlide, y ) { + + sortFragments( verticalSlide.querySelectorAll( '.fragment' ) ); + + } ); + + if( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) ); + + } ); + + } + + /** + * Randomly shuffles all slides in the deck. + */ + function shuffle() { + + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + slides.forEach( function( slide ) { + + // Insert this slide next to another random slide. This may + // cause the slide to insert before itself but that's fine. + dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] ); + + } ); + + } + + /** + * Updates one dimension of slides by showing the slide + * with the specified index. + * + * @param {string} selector A CSS selector that will fetch + * the group of slides we are working with + * @param {number} index The index of the slide that should be + * shown + * + * @return {number} The index of the slide that is now shown, + * might differ from the passed in index if it was out of + * bounds. + */ + function updateSlides( selector, index ) { + + // Select all slides and convert the NodeList result to + // an array + var slides = toArray( dom.wrapper.querySelectorAll( selector ) ), + slidesLength = slides.length; + + var printMode = isPrintingPDF(); + + if( slidesLength ) { + + // Should the index loop? + if( config.loop ) { + index %= slidesLength; + + if( index < 0 ) { + index = slidesLength + index; + } + } + + // Enforce max and minimum index bounds + index = Math.max( Math.min( index, slidesLength - 1 ), 0 ); + + for( var i = 0; i < slidesLength; i++ ) { + var element = slides[i]; + + var reverse = config.rtl && !isVerticalSlide( element ); + + element.classList.remove( 'past' ); + element.classList.remove( 'present' ); + element.classList.remove( 'future' ); + + // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute + element.setAttribute( 'hidden', '' ); + element.setAttribute( 'aria-hidden', 'true' ); + + // If this element contains vertical slides + if( element.querySelector( 'section' ) ) { + element.classList.add( 'stack' ); + } + + // If we're printing static slides, all slides are "present" + if( printMode ) { + element.classList.add( 'present' ); + continue; + } + + if( i < index ) { + // Any element previous to index is given the 'past' class + element.classList.add( reverse ? 'future' : 'past' ); + + if( config.fragments ) { + var pastFragments = toArray( element.querySelectorAll( '.fragment' ) ); + + // Show all fragments on prior slides + while( pastFragments.length ) { + var pastFragment = pastFragments.pop(); + pastFragment.classList.add( 'visible' ); + pastFragment.classList.remove( 'current-fragment' ); + } + } + } + else if( i > index ) { + // Any element subsequent to index is given the 'future' class + element.classList.add( reverse ? 'past' : 'future' ); + + if( config.fragments ) { + var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) ); + + // No fragments in future slides should be visible ahead of time + while( futureFragments.length ) { + var futureFragment = futureFragments.pop(); + futureFragment.classList.remove( 'visible' ); + futureFragment.classList.remove( 'current-fragment' ); + } + } + } + } + + // Mark the current slide as present + slides[index].classList.add( 'present' ); + slides[index].removeAttribute( 'hidden' ); + slides[index].removeAttribute( 'aria-hidden' ); + + // If this slide has a state associated with it, add it + // onto the current state of the deck + var slideState = slides[index].getAttribute( 'data-state' ); + if( slideState ) { + state = state.concat( slideState.split( ' ' ) ); + } + + } + else { + // Since there are no slides we can't be anywhere beyond the + // zeroth index + index = 0; + } + + return index; + + } + + /** + * Optimization method; hide all slides that are far away + * from the present slide. + */ + function updateSlidesVisibility() { + + // Select all slides and convert the NodeList result to + // an array + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ), + horizontalSlidesLength = horizontalSlides.length, + distanceX, + distanceY; + + if( horizontalSlidesLength && typeof indexh !== 'undefined' ) { + + // The number of steps away from the present slide that will + // be visible + var viewDistance = isOverview() ? 10 : config.viewDistance; + + // Limit view distance on weaker devices + if( isMobileDevice ) { + viewDistance = isOverview() ? 6 : 2; + } + + // All slides need to be visible when exporting to PDF + if( isPrintingPDF() ) { + viewDistance = Number.MAX_VALUE; + } + + for( var x = 0; x < horizontalSlidesLength; x++ ) { + var horizontalSlide = horizontalSlides[x]; + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ), + verticalSlidesLength = verticalSlides.length; + + // Determine how far away this slide is from the present + distanceX = Math.abs( ( indexh || 0 ) - x ) || 0; + + // If the presentation is looped, distance should measure + // 1 between the first and last slides + if( config.loop ) { + distanceX = Math.abs( ( ( indexh || 0 ) - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0; + } + + // Show the horizontal slide if it's within the view distance + if( distanceX < viewDistance ) { + loadSlide( horizontalSlide ); + } + else { + unloadSlide( horizontalSlide ); + } + + if( verticalSlidesLength ) { + + var oy = getPreviousVerticalIndex( horizontalSlide ); + + for( var y = 0; y < verticalSlidesLength; y++ ) { + var verticalSlide = verticalSlides[y]; + + distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy ); + + if( distanceX + distanceY < viewDistance ) { + loadSlide( verticalSlide ); + } + else { + unloadSlide( verticalSlide ); + } + } + + } + } + + // Flag if there are ANY vertical slides, anywhere in the deck + if( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) { + dom.wrapper.classList.add( 'has-vertical-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-vertical-slides' ); + } + + // Flag if there are ANY horizontal slides, anywhere in the deck + if( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) { + dom.wrapper.classList.add( 'has-horizontal-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-horizontal-slides' ); + } + + } + + } + + /** + * Pick up notes from the current slide and display them + * to the viewer. + * + * @see {@link config.showNotes} + */ + function updateNotes() { + + if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) { + + dom.speakerNotes.innerHTML = getSlideNotes() || 'No notes on this slide.'; + + } + + } + + /** + * Updates the visibility of the speaker notes sidebar that + * is used to share annotated slides. The notes sidebar is + * only visible if showNotes is true and there are notes on + * one or more slides in the deck. + */ + function updateNotesVisibility() { + + if( config.showNotes && hasNotes() ) { + dom.wrapper.classList.add( 'show-notes' ); + } + else { + dom.wrapper.classList.remove( 'show-notes' ); + } + + } + + /** + * Checks if there are speaker notes for ANY slide in the + * presentation. + */ + function hasNotes() { + + return dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0; + + } + + /** + * Updates the progress bar to reflect the current slide. + */ + function updateProgress() { + + // Update progress if enabled + if( config.progress && dom.progressbar ) { + + dom.progressbar.style.width = getProgress() * dom.wrapper.offsetWidth + 'px'; + + } + + } + + /** + * Updates the slide number div to reflect the current slide. + * + * The following slide number formats are available: + * "h.v": horizontal . vertical slide number (default) + * "h/v": horizontal / vertical slide number + * "c": flattened slide number + * "c/t": flattened slide number / total slides + */ + function updateSlideNumber() { + + // Update slide number if enabled + if( config.slideNumber && dom.slideNumber ) { + + var value = []; + var format = 'h.v'; + + // Check if a custom number format is available + if( typeof config.slideNumber === 'string' ) { + format = config.slideNumber; + } + + switch( format ) { + case 'c': + value.push( getSlidePastCount() + 1 ); + break; + case 'c/t': + value.push( getSlidePastCount() + 1, '/', getTotalSlides() ); + break; + case 'h/v': + value.push( indexh + 1 ); + if( isVerticalSlide() ) value.push( '/', indexv + 1 ); + break; + default: + value.push( indexh + 1 ); + if( isVerticalSlide() ) value.push( '.', indexv + 1 ); + } + + dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] ); + } + + } + + /** + * Applies HTML formatting to a slide number before it's + * written to the DOM. + * + * @param {number} a Current slide + * @param {string} delimiter Character to separate slide numbers + * @param {(number|*)} b Total slides + * @return {string} HTML string fragment + */ + function formatSlideNumber( a, delimiter, b ) { + + if( typeof b === 'number' && !isNaN( b ) ) { + return ''+ a +'' + + ''+ delimiter +'' + + ''+ b +''; + } + else { + return ''+ a +''; + } + + } + + /** + * Updates the state of all control/navigation arrows. + */ + function updateControls() { + + var routes = availableRoutes(); + var fragments = availableFragments(); + + // Remove the 'enabled' class from all directions + dom.controlsLeft.concat( dom.controlsRight ) + .concat( dom.controlsUp ) + .concat( dom.controlsDown ) + .concat( dom.controlsPrev ) + .concat( dom.controlsNext ).forEach( function( node ) { + node.classList.remove( 'enabled' ); + node.classList.remove( 'fragmented' ); + + // Set 'disabled' attribute on all directions + node.setAttribute( 'disabled', 'disabled' ); + } ); + + // Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons + if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Prev/next buttons + if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Highlight fragment directions + if( currentSlide ) { + + // Always apply fragment decorator to prev/next buttons + if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Apply fragment decorators to directional buttons based on + // what slide axis they are in + if( isVerticalSlide( currentSlide ) ) { + if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + else { + if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + + } + + if( config.controlsTutorial ) { + + // Highlight control arrows with an animation to ensure + // that the viewer knows how to navigate + if( !hasNavigatedDown && routes.down ) { + dom.controlsDownArrow.classList.add( 'highlight' ); + } + else { + dom.controlsDownArrow.classList.remove( 'highlight' ); + + if( !hasNavigatedRight && routes.right && indexv === 0 ) { + dom.controlsRightArrow.classList.add( 'highlight' ); + } + else { + dom.controlsRightArrow.classList.remove( 'highlight' ); + } + } + + } + + } + + /** + * Updates the background elements to reflect the current + * slide. + * + * @param {boolean} includeAll If true, the backgrounds of + * all vertical slides (not just the present) will be updated. + */ + function updateBackground( includeAll ) { + + var currentBackground = null; + + // Reverse past/future classes when in RTL mode + var horizontalPast = config.rtl ? 'future' : 'past', + horizontalFuture = config.rtl ? 'past' : 'future'; + + // Update the classes of all backgrounds to match the + // states of their slides (past/present/future) + toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) { + + backgroundh.classList.remove( 'past' ); + backgroundh.classList.remove( 'present' ); + backgroundh.classList.remove( 'future' ); + + if( h < indexh ) { + backgroundh.classList.add( horizontalPast ); + } + else if ( h > indexh ) { + backgroundh.classList.add( horizontalFuture ); + } + else { + backgroundh.classList.add( 'present' ); + + // Store a reference to the current background element + currentBackground = backgroundh; + } + + if( includeAll || h === indexh ) { + toArray( backgroundh.querySelectorAll( '.slide-background' ) ).forEach( function( backgroundv, v ) { + + backgroundv.classList.remove( 'past' ); + backgroundv.classList.remove( 'present' ); + backgroundv.classList.remove( 'future' ); + + if( v < indexv ) { + backgroundv.classList.add( 'past' ); + } + else if ( v > indexv ) { + backgroundv.classList.add( 'future' ); + } + else { + backgroundv.classList.add( 'present' ); + + // Only if this is the present horizontal and vertical slide + if( h === indexh ) currentBackground = backgroundv; + } + + } ); + } + + } ); + + // Stop content inside of previous backgrounds + if( previousBackground ) { + + stopEmbeddedContent( previousBackground ); + + } + + // Start content in the current background + if( currentBackground ) { + + startEmbeddedContent( currentBackground ); + + var backgroundImageURL = currentBackground.style.backgroundImage || ''; + + // Restart GIFs (doesn't work in Firefox) + if( /\.gif/i.test( backgroundImageURL ) ) { + currentBackground.style.backgroundImage = ''; + window.getComputedStyle( currentBackground ).opacity; + currentBackground.style.backgroundImage = backgroundImageURL; + } + + // Don't transition between identical backgrounds. This + // prevents unwanted flicker. + var previousBackgroundHash = previousBackground ? previousBackground.getAttribute( 'data-background-hash' ) : null; + var currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' ); + if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== previousBackground ) { + dom.background.classList.add( 'no-transition' ); + } + + previousBackground = currentBackground; + + } + + // If there's a background brightness flag for this slide, + // bubble it to the .reveal container + if( currentSlide ) { + [ 'has-light-background', 'has-dark-background' ].forEach( function( classToBubble ) { + if( currentSlide.classList.contains( classToBubble ) ) { + dom.wrapper.classList.add( classToBubble ); + } + else { + dom.wrapper.classList.remove( classToBubble ); + } + } ); + } + + // Allow the first background to apply without transition + setTimeout( function() { + dom.background.classList.remove( 'no-transition' ); + }, 1 ); + + } + + /** + * Updates the position of the parallax background based + * on the current slide index. + */ + function updateParallax() { + + if( config.parallaxBackgroundImage ) { + + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), + verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); + + var backgroundSize = dom.background.style.backgroundSize.split( ' ' ), + backgroundWidth, backgroundHeight; + + if( backgroundSize.length === 1 ) { + backgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 ); + } + else { + backgroundWidth = parseInt( backgroundSize[0], 10 ); + backgroundHeight = parseInt( backgroundSize[1], 10 ); + } + + var slideWidth = dom.background.offsetWidth, + horizontalSlideCount = horizontalSlides.length, + horizontalOffsetMultiplier, + horizontalOffset; + + if( typeof config.parallaxBackgroundHorizontal === 'number' ) { + horizontalOffsetMultiplier = config.parallaxBackgroundHorizontal; + } + else { + horizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0; + } + + horizontalOffset = horizontalOffsetMultiplier * indexh * -1; + + var slideHeight = dom.background.offsetHeight, + verticalSlideCount = verticalSlides.length, + verticalOffsetMultiplier, + verticalOffset; + + if( typeof config.parallaxBackgroundVertical === 'number' ) { + verticalOffsetMultiplier = config.parallaxBackgroundVertical; + } + else { + verticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ); + } + + verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv : 0; + + dom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px'; + + } + + } + + /** + * Called when the given slide is within the configured view + * distance. Shows the slide element and loads any content + * that is set to load lazily (data-src). + * + * @param {HTMLElement} slide Slide to show + */ + function loadSlide( slide, options ) { + + options = options || {}; + + // Show the slide element + slide.style.display = config.display; + + // Media elements with data-src attributes + toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) { + element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.setAttribute( 'data-lazy-loaded', '' ); + element.removeAttribute( 'data-src' ); + } ); + + // Media elements with children + toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( media ) { + var sources = 0; + + toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) { + source.setAttribute( 'src', source.getAttribute( 'data-src' ) ); + source.removeAttribute( 'data-src' ); + source.setAttribute( 'data-lazy-loaded', '' ); + sources += 1; + } ); + + // If we rewrote sources for this video/audio element, we need + // to manually tell it to load from its new origin + if( sources > 0 ) { + media.load(); + } + } ); + + + // Show the corresponding background element + var indices = getIndices( slide ); + var background = getSlideBackground( indices.h, indices.v ); + if( background ) { + background.style.display = 'block'; + + // If the background contains media, load it + if( background.hasAttribute( 'data-loaded' ) === false ) { + background.setAttribute( 'data-loaded', 'true' ); + + var backgroundImage = slide.getAttribute( 'data-background-image' ), + backgroundVideo = slide.getAttribute( 'data-background-video' ), + backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ), + backgroundIframe = slide.getAttribute( 'data-background-iframe' ); + + // Images + if( backgroundImage ) { + background.style.backgroundImage = 'url('+ backgroundImage +')'; + } + // Videos + else if ( backgroundVideo && !isSpeakerNotes() ) { + var video = document.createElement( 'video' ); + + if( backgroundVideoLoop ) { + video.setAttribute( 'loop', '' ); + } + + if( backgroundVideoMuted ) { + video.muted = true; + } + + // Inline video playback works (at least in Mobile Safari) as + // long as the video is muted and the `playsinline` attribute is + // present + if( isMobileDevice ) { + video.muted = true; + video.autoplay = true; + video.setAttribute( 'playsinline', '' ); + } + + // Support comma separated lists of video sources + backgroundVideo.split( ',' ).forEach( function( source ) { + video.innerHTML += ''; + } ); + + background.appendChild( video ); + } + // Iframes + else if( backgroundIframe && options.excludeIframes !== true ) { + var iframe = document.createElement( 'iframe' ); + iframe.setAttribute( 'allowfullscreen', '' ); + iframe.setAttribute( 'mozallowfullscreen', '' ); + iframe.setAttribute( 'webkitallowfullscreen', '' ); + + // Only load autoplaying content when the slide is shown to + // avoid having it play in the background + if( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { + iframe.setAttribute( 'data-src', backgroundIframe ); + } + else { + iframe.setAttribute( 'src', backgroundIframe ); + } + + iframe.style.width = '100%'; + iframe.style.height = '100%'; + iframe.style.maxHeight = '100%'; + iframe.style.maxWidth = '100%'; + + background.appendChild( iframe ); + } + } + + } + + } + + /** + * Unloads and hides the given slide. This is called when the + * slide is moved outside of the configured view distance. + * + * @param {HTMLElement} slide + */ + function unloadSlide( slide ) { + + // Hide the slide element + slide.style.display = 'none'; + + // Hide the corresponding background element + var indices = getIndices( slide ); + var background = getSlideBackground( indices.h, indices.v ); + if( background ) { + background.style.display = 'none'; + } + + // Reset lazy-loaded media elements with src attributes + toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) { + element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); + element.removeAttribute( 'src' ); + } ); + + // Reset lazy-loaded media elements with children + toArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) { + source.setAttribute( 'data-src', source.getAttribute( 'src' ) ); + source.removeAttribute( 'src' ); + } ); + + } + + /** + * Determine what available routes there are for navigation. + * + * @return {{left: boolean, right: boolean, up: boolean, down: boolean}} + */ + function availableRoutes() { + + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), + verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); + + var routes = { + left: indexh > 0 || config.loop, + right: indexh < horizontalSlides.length - 1 || config.loop, + up: indexv > 0, + down: indexv < verticalSlides.length - 1 + }; + + // reverse horizontal controls for rtl + if( config.rtl ) { + var left = routes.left; + routes.left = routes.right; + routes.right = left; + } + + return routes; + + } + + /** + * Returns an object describing the available fragment + * directions. + * + * @return {{prev: boolean, next: boolean}} + */ + function availableFragments() { + + if( currentSlide && config.fragments ) { + var fragments = currentSlide.querySelectorAll( '.fragment' ); + var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' ); + + return { + prev: fragments.length - hiddenFragments.length > 0, + next: !!hiddenFragments.length + }; + } + else { + return { prev: false, next: false }; + } + + } + + /** + * Enforces origin-specific format rules for embedded media. + */ + function formatEmbeddedContent() { + + var _appendParamToIframeSource = function( sourceAttribute, sourceURL, param ) { + toArray( dom.slides.querySelectorAll( 'iframe['+ sourceAttribute +'*="'+ sourceURL +'"]' ) ).forEach( function( el ) { + var src = el.getAttribute( sourceAttribute ); + if( src && src.indexOf( param ) === -1 ) { + el.setAttribute( sourceAttribute, src + ( !/\?/.test( src ) ? '?' : '&' ) + param ); + } + }); + }; + + // YouTube frames must include "?enablejsapi=1" + _appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' ); + _appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' ); + + // Vimeo frames must include "?api=1" + _appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' ); + _appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' ); + + // Always show media controls on mobile devices + if( isMobileDevice ) { + toArray( dom.slides.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + el.controls = true; + } ); + } + + } + + /** + * Start playback of any embedded content inside of + * the given element. + * + * @param {HTMLElement} element + */ + function startEmbeddedContent( element ) { + + if( element && !isSpeakerNotes() ) { + + // Restart GIFs + toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { + // Setting the same unchanged source like this was confirmed + // to work in Chrome, FF & Safari + el.setAttribute( 'src', el.getAttribute( 'src' ) ); + } ); + + // HTML5 media elements + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' ); + } + + if( autoplay && typeof el.play === 'function' ) { + + if( el.readyState > 1 ) { + startEmbeddedMedia( { target: el } ); + } + else { + el.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes + el.addEventListener( 'loadeddata', startEmbeddedMedia ); + } + + } + } ); + + // Normal iframes + toArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + startEmbeddedIframe( { target: el } ); + } ); + + // Lazy loading iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) { + el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes + el.addEventListener( 'load', startEmbeddedIframe ); + el.setAttribute( 'src', el.getAttribute( 'data-src' ) ); + } + } ); + + } + + } + + /** + * Starts playing an embedded video/audio element after + * it has finished loading. + * + * @param {object} event + */ + function startEmbeddedMedia( event ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + event.target.currentTime = 0; + event.target.play(); + } + + event.target.removeEventListener( 'loadeddata', startEmbeddedMedia ); + + } + + /** + * "Starts" the content of an embedded iframe using the + * postMessage API. + * + * @param {object} event + */ + function startEmbeddedIframe( event ) { + + var iframe = event.target; + + if( iframe && iframe.contentWindow ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' ); + } + + // YouTube postMessage API + if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); + } + // Vimeo postMessage API + else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); + } + // Generic postMessage API + else { + iframe.contentWindow.postMessage( 'slide:start', '*' ); + } + + } + + } + + } + + /** + * Stop playback of any embedded content inside of + * the targeted slide. + * + * @param {HTMLElement} element + */ + function stopEmbeddedContent( element, options ) { + + options = extend( { + // Defaults + unloadIframes: true + }, options || {} ); + + if( element && element.parentNode ) { + // HTML5 media elements + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) { + el.setAttribute('data-paused-by-reveal', ''); + el.pause(); + } + } ); + + // Generic postMessage API for non-lazy loaded iframes + toArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) { + if( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' ); + el.removeEventListener( 'load', startEmbeddedIframe ); + }); + + // YouTube postMessage API + toArray( element.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { + el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' ); + } + }); + + // Vimeo postMessage API + toArray( element.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { + el.contentWindow.postMessage( '{"method":"pause"}', '*' ); + } + }); + + if( options.unloadIframes === true ) { + // Unload lazy-loaded iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + // Only removing the src doesn't actually unload the frame + // in all browsers (Firefox) so we set it to blank first + el.setAttribute( 'src', 'about:blank' ); + el.removeAttribute( 'src' ); + } ); + } + } + + } + + /** + * Returns the number of past slides. This can be used as a global + * flattened index for slides. + * + * @return {number} Past slide count + */ + function getSlidePastCount() { + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // The number of past slides + var pastCount = 0; + + // Step through all slides and count the past ones + mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { + + var horizontalSlide = horizontalSlides[i]; + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + + for( var j = 0; j < verticalSlides.length; j++ ) { + + // Stop as soon as we arrive at the present + if( verticalSlides[j].classList.contains( 'present' ) ) { + break mainLoop; + } + + pastCount++; + + } + + // Stop as soon as we arrive at the present + if( horizontalSlide.classList.contains( 'present' ) ) { + break; + } + + // Don't count the wrapping section for vertical slides + if( horizontalSlide.classList.contains( 'stack' ) === false ) { + pastCount++; + } + + } + + return pastCount; + + } + + /** + * Returns a value ranging from 0-1 that represents + * how far into the presentation we have navigated. + * + * @return {number} + */ + function getProgress() { + + // The number of past and total slides + var totalCount = getTotalSlides(); + var pastCount = getSlidePastCount(); + + if( currentSlide ) { + + var allFragments = currentSlide.querySelectorAll( '.fragment' ); + + // If there are fragments in the current slide those should be + // accounted for in the progress. + if( allFragments.length > 0 ) { + var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' ); + + // This value represents how big a portion of the slide progress + // that is made up by its fragments (0-1) + var fragmentWeight = 0.9; + + // Add fragment progress to the past slide count + pastCount += ( visibleFragments.length / allFragments.length ) * fragmentWeight; + } + + } + + return pastCount / ( totalCount - 1 ); + + } + + /** + * Checks if this presentation is running inside of the + * speaker notes window. + * + * @return {boolean} + */ + function isSpeakerNotes() { + + return !!window.location.search.match( /receiver/gi ); + + } + + /** + * Reads the current URL (hash) and navigates accordingly. + */ + function readURL() { + + var hash = window.location.hash; + + // Attempt to parse the hash as either an index or name + var bits = hash.slice( 2 ).split( '/' ), + name = hash.replace( /#|\//gi, '' ); + + // If the first bit is invalid and there is a name we can + // assume that this is a named link + if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) { + var element; + + // Ensure the named link is a valid HTML ID attribute + if( /^[a-zA-Z][\w:.-]*$/.test( name ) ) { + // Find the slide with the specified ID + element = document.getElementById( name ); + } + + if( element ) { + // Find the position of the named slide and navigate to it + var indices = Reveal.getIndices( element ); + slide( indices.h, indices.v ); + } + // If the slide doesn't exist, navigate to the current slide + else { + slide( indexh || 0, indexv || 0 ); + } + } + else { + // Read the index components of the hash + var h = parseInt( bits[0], 10 ) || 0, + v = parseInt( bits[1], 10 ) || 0; + + if( h !== indexh || v !== indexv ) { + slide( h, v ); + } + } + + } + + /** + * Updates the page URL (hash) to reflect the current + * state. + * + * @param {number} delay The time in ms to wait before + * writing the hash + */ + function writeURL( delay ) { + + if( config.history ) { + + // Make sure there's never more than one timeout running + clearTimeout( writeURLTimeout ); + + // If a delay is specified, timeout this call + if( typeof delay === 'number' ) { + writeURLTimeout = setTimeout( writeURL, delay ); + } + else if( currentSlide ) { + var url = '/'; + + // Attempt to create a named link based on the slide's ID + var id = currentSlide.getAttribute( 'id' ); + if( id ) { + id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' ); + } + + // If the current slide has an ID, use that as a named link + if( typeof id === 'string' && id.length ) { + url = '/' + id; + } + // Otherwise use the /h/v index + else { + if( indexh > 0 || indexv > 0 ) url += indexh; + if( indexv > 0 ) url += '/' + indexv; + } + + window.location.hash = url; + } + } + + } + /** + * Retrieves the h/v location and fragment of the current, + * or specified, slide. + * + * @param {HTMLElement} [slide] If specified, the returned + * index will be for this slide rather than the currently + * active one + * + * @return {{h: number, v: number, f: number}} + */ + function getIndices( slide ) { + + // By default, return the current indices + var h = indexh, + v = indexv, + f; + + // If a slide is specified, return the indices of that slide + if( slide ) { + var isVertical = isVerticalSlide( slide ); + var slideh = isVertical ? slide.parentNode : slide; + + // Select all horizontal slides + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // Now that we know which the horizontal slide is, get its index + h = Math.max( horizontalSlides.indexOf( slideh ), 0 ); + + // Assume we're not vertical + v = undefined; + + // If this is a vertical slide, grab the vertical index + if( isVertical ) { + v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 ); + } + } + + if( !slide && currentSlide ) { + var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0; + if( hasFragments ) { + var currentFragment = currentSlide.querySelector( '.current-fragment' ); + if( currentFragment && currentFragment.hasAttribute( 'data-fragment-index' ) ) { + f = parseInt( currentFragment.getAttribute( 'data-fragment-index' ), 10 ); + } + else { + f = currentSlide.querySelectorAll( '.fragment.visible' ).length - 1; + } + } + } + + return { h: h, v: v, f: f }; + + } + + /** + * Retrieves all slides in this presentation. + */ + function getSlides() { + + return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' )); + + } + + /** + * Retrieves the total number of slides in this presentation. + * + * @return {number} + */ + function getTotalSlides() { + + return getSlides().length; + + } + + /** + * Returns the slide element matching the specified index. + * + * @return {HTMLElement} + */ + function getSlide( x, y ) { + + var horizontalSlide = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ]; + var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' ); + + if( verticalSlides && verticalSlides.length && typeof y === 'number' ) { + return verticalSlides ? verticalSlides[ y ] : undefined; + } + + return horizontalSlide; + + } + + /** + * Returns the background element for the given slide. + * All slides, even the ones with no background properties + * defined, have a background element so as long as the + * index is valid an element will be returned. + * + * @param {number} x Horizontal background index + * @param {number} y Vertical background index + * @return {(HTMLElement[]|*)} + */ + function getSlideBackground( x, y ) { + + var slide = getSlide( x, y ); + if( slide ) { + return slide.slideBackgroundElement; + } + + return undefined; + + } + + /** + * Retrieves the speaker notes from a slide. Notes can be + * defined in two ways: + * 1. As a data-notes attribute on the slide
+ * 2. As an
+ +
+

The translation of IP Addresses to Domain Names is handled by a bunch of special Servers, called Domain Name System Servers or DNS Servers for short. Think of them as the old Telephone Operators.

+ +
+ @@ -325,55 +334,63 @@

IP Addresses & Domain Name Servers

Network Ports

-

...

+

One Address - Many Services

+
+

+ Whenever we enter an URL on our Browser's address bar in order to reach a specific website, a hidden number is present in the URL right after the domain name, in the following form: +

- - - Web Server* -
- *A Server, holding and serving web pages.
( It can 'talk' HTTP )
-

- -   - +

What we type:

+ www.domain.com + www.cnn.com/news
- - - Your Web Browser* -
- *Software that fetches and displays web pages.
( It can 'talk' HTTP too )
-

-
+

The actual request:

+ www.domain.com:80 + www.cnn.com:80/news +
- - <--[ GET /index.html ]-- - -
- - The Web Browser, requests the index.html file
from the Web Server using a simple HTTP command: GET -
-

- - --[ index.html ]--> - -
- - The Web Server, responds by sending the index.html
file to the Web Browser, which in turn renders
and displays the web page to the user. -
+

Try it! reach your favorite site by appending a :80 right after its domain name.

+ +
+

But, what exactly is the purpose of such a number?

+
+ +
+

A Network Port number can be compared with a Call Centre's internal phone numbers.

+
+ +
+ +
+ +
+

An unique external telephone number can be used as the first point of entrance into the Call Centre (think of an IP/Domain Name analogy here).

+

The Internal Numbers (think of Network Ports) can subsequently be used to connect to the many different internal endpoints/services.

+
+ +
+ +
+ +
+

Much like a Call Centre, a Server using one unique IP address or Domain Name can provide many different services, each using an unique Network Port.

+

For example, a Server might serve web pages on Port 80 and also run a Chat Service (like Skype) on a different Port, e.g. 4378

+

A Server can have a maximum of 65535 Network Port numbers.

+
+
-

That's all Folks!

From eba7eaadb07c6e36ead8cc70ecc228bbf3264ebe Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Apr 2018 17:45:36 +0300 Subject: [PATCH 069/227] Typo --- WebOversimplified/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 75673f4..5a13441 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -243,7 +243,7 @@

IP Addresses & Domain Name Servers

Computers on the Internet contact each other much like our mobile phones.

- Each mobile device is using a unique phone number
which enables it to place and receive calls.
+ Each mobile device is using an unique phone number
which enables it to place and receive calls.
From 8a7e3857cf659fa47f49998e95bebca0a17cb270 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Apr 2018 17:50:33 +0300 Subject: [PATCH 070/227] Adding some fade ins. --- WebOversimplified/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 5a13441..820363d 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -296,7 +296,9 @@

IP Addresses & Domain Name Servers

Bob === 6912345678
-

Bob has a Contact entry named "Alice" on his phone
that points to the number of Alice.
Whenever he wants to call Alice, he just types "Alice"
instead of the number.

+

Bob has a Contact entry named Alice on his phone
that points to the number of Alice.

+ +

Whenever he wants to call Alice, he just types Alice
instead of the number.

From ecd270394ed4aeb3ef16f78d38b6e53b348a3cd4 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Apr 2018 17:55:20 +0300 Subject: [PATCH 071/227] Changing title --- WebOversimplified/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 820363d..fb8b5d4 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -4,7 +4,7 @@ - JavaScript Module 1 | Week 02 Part II | Social Hackers Academy + Node.JS | The Web Oversimplified | Social Hackers Academy From 178f60b643df6b5ac0afd47e8ea656183e024fa1 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Apr 2018 18:07:42 +0300 Subject: [PATCH 072/227] Adding Recap --- WebOversimplified/index.html | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index fb8b5d4..196c1a6 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -391,11 +391,35 @@

One Address - Many Services

+ +
+

RECAP:

+
    +
  • Whenever we open a new Browser window and enter an URL on the address bar, our computer connects to another computer, we call a Web Server. The two computers can reach each other through their two unique numbers called IP addresses. +

  • +
  • When we request a web page through an URL, the Web Server responds by sending us an HTML file that our Browser parses and displays on our Browser's window, e.g. a request to www.domain.com/contact.html will result in the HTML file contact.html being downloaded and rendered by our Browser. +

  • +
  • Since names are more descriptive than numbers, and can be easily memorized, we tend to use Domain Names that correspond to IP addresses, much like our Phones store telephone numbers using Contact names.
  • +
+
+ + +
+

RECAP:

+
    +
  • Special Servers, called DNS Servers are used to translate the IP Addresses to Domain Names. +

  • +
  • Each Server, can have one unique IP Address and still offer many different Internet services through the use of Network Port Numbers. Different Client Software, such as Web Browsers, Skype, etc., can then use this Network Port numbers to reach these different services. +

  • +
+
+

That's all Folks!

+ From 29576046bead1bc181de2a972167e105782fdc43 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 22 Apr 2018 01:40:17 +0300 Subject: [PATCH 073/227] Typo --- WebOversimplified/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 196c1a6..26eeeeb 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -262,7 +262,7 @@

IP Addresses & Domain Name Servers

-

Instad of telephone numbers, computers use Internet Protocol (IP) Addresses.

+

Instead of telephone numbers, computers use Internet Protocol (IP) Addresses.

From 9385a39d55fd588caf84c0eac7302b8361479bef Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 22 Apr 2018 01:57:40 +0300 Subject: [PATCH 074/227] Typo --- WebOversimplified/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 26eeeeb..98611c8 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -243,7 +243,7 @@

IP Addresses & Domain Name Servers

Computers on the Internet contact each other much like our mobile phones.

- Each mobile device is using an unique phone number
which enables it to place and receive calls.
+ Each mobile device is using a unique phone number
which enables it to place and receive calls.
@@ -375,7 +375,7 @@

One Address - Many Services

-

An unique external telephone number can be used as the first point of entrance into the Call Centre (think of an IP/Domain Name analogy here).

+

A unique external telephone number can be used as the first point of entrance into the Call Centre (think of an IP/Domain Name analogy here).

The Internal Numbers (think of Network Ports) can subsequently be used to connect to the many different internal endpoints/services.

@@ -384,7 +384,7 @@

One Address - Many Services

-

Much like a Call Centre, a Server using one unique IP address or Domain Name can provide many different services, each using an unique Network Port.

+

Much like a Call Centre, a Server using one unique IP address or Domain Name can provide many different services, each using a unique Network Port.

For example, a Server might serve web pages on Port 80 and also run a Chat Service (like Skype) on a different Port, e.g. 4378

A Server can have a maximum of 65535 Network Port numbers.

From 47bd4dd987ab3e425aaa64bcbe5ea46a93bd5b39 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 22 Apr 2018 01:59:19 +0300 Subject: [PATCH 075/227] Typo --- WebOversimplified/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebOversimplified/index.html b/WebOversimplified/index.html index 98611c8..dec6091 100644 --- a/WebOversimplified/index.html +++ b/WebOversimplified/index.html @@ -395,9 +395,9 @@

One Address - Many Services

RECAP:

    -
  • Whenever we open a new Browser window and enter an URL on the address bar, our computer connects to another computer, we call a Web Server. The two computers can reach each other through their two unique numbers called IP addresses. +
  • Whenever we open a new Browser window and enter a URL on the address bar, our computer connects to another computer, we call a Web Server. The two computers can reach each other through their two unique numbers called IP addresses.

  • -
  • When we request a web page through an URL, the Web Server responds by sending us an HTML file that our Browser parses and displays on our Browser's window, e.g. a request to www.domain.com/contact.html will result in the HTML file contact.html being downloaded and rendered by our Browser. +
  • When we request a web page through a URL, the Web Server responds by sending us an HTML file that our Browser parses and displays on our Browser's window, e.g. a request to www.domain.com/contact.html will result in the HTML file contact.html being downloaded and rendered by our Browser.

  • Since names are more descriptive than numbers, and can be easily memorized, we tend to use Domain Names that correspond to IP addresses, much like our Phones store telephone numbers using Contact names.
From 56498d09eada80d85139dc197e098ece579e8876 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 19 Jun 2019 23:30:33 +0300 Subject: [PATCH 076/227] Updating variables tip --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1ebed6..f960e64 100755 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ### 2. SUGGESTED CODING RULES AND BEST PRACTICES -* Always use `var` when declaring a variable +* Always use `let` or `const` when declaring variables. Avoid using `var`. * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonley Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) * [Prefer using `===` over `==`](http://bonsaiden.github.io/JavaScript-Garden/#types.equality) From 46ca7b6358ff0c3756663ce957b2e91091c242ac Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 19 Jun 2019 23:37:20 +0300 Subject: [PATCH 077/227] Updating resources --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f960e64..b5d38a1 100755 --- a/README.md +++ b/README.md @@ -104,6 +104,9 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) * [JavaScript Guide, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) +* [Introduction to JavaScript - 24 Interactive Screencasts](https://scrimba.com/g/gintrotojavascript) +* [Learn modern JavaScript - ES6 through Interactive Screencasts](https://scrimba.com/g/ges6) +* [Introduction to ES6+ - 23 Interactive Screencasts](https://scrimba.com/g/gintrotoes6) * [JAVASCRIPT FUNDAMENTALS, *by Azat Mardan*](http://gist.io/5955726) * [5 Tips to Become a Better JavaScript Developer, *by JUSTIN CHMURA*](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * [JS 101](http://www.teaching-materials.org/javascript/) @@ -388,6 +391,11 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Javascript, the Good Parts, GoogleTechTalks, *Douglas Crockford*](https://www.youtube.com/watch?v=hQVTIJBZook) * [Douglas Crockford: The JavaScript Programming Language](https://www.youtube.com/watch?v=v2ifWcnQs6M) + * [Vanilla JavaScript YouTube Playlist by Traversy Media](https://www.youtube.com/watch?v=hdI2bqOjy3c&list=PLillGF-RfqbbnEGy3ROiLWk7JMCuSyQtX) + * [ES6 Playlist by Traversy Media](https://www.youtube.com/watch?v=2LeqilIw-28&list=PLillGF-RfqbZ7s3t6ZInY3NjEOOX7hsBv) + * [TreeHouse - Beginner's Guide to JavaScript](https://www.youtube.com/watch?v=UOeofWla8mE) + * [Modern JavaScript YouTuybe Playlist By NetNinja](https://www.youtube.com/playlist?list=PL4cUxeGkcC9haFPT7J25Q9GRB_ZkFrQAc) + * [OOP in JavaScript | YouTube Playlist by NetNinja](https://www.youtube.com/playlist?list=PL4cUxeGkcC9i5yvDkJgt60vNVWffpblB7) * [10 Things I Learned from the jQuery Source, *Paul Irish*](http://www.youtube.com/watch?v=i_qE1iAmjFg) * [11 More Things I Learned from the jQuery Source, *Paul Irish*](http://www.youtube.com/watch?v=ARnp9Y8xgR4) * [Paul Irish, "Delivering the goods"](https://www.youtube.com/watch?v=R8W_6xWphtw) From cee8ce419f582950c1ed25031ad986450d80fe47 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 19 Jun 2019 23:42:24 +0300 Subject: [PATCH 078/227] Updating Blog link for Axel Rauschmayer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5d38a1..8d59485 100755 --- a/README.md +++ b/README.md @@ -427,7 +427,7 @@ Azat Mardan | [Twitter](https://twitter.com/azat_co) | | Alex Castrounis | [Twitter](https://twitter.com/innoarchitech) | [Blog](http://www.innoarchitech.com/) | Aria Stewart | [Twitter](https://twitter.com/aredridel) | [Blog](http://dinhe.net/~aredridel/) | Ariya Hidayat | [Twitter](http://twitter.com/ariyahidayat) | [Blog](http://ariya.ofilabs.com/) | [YouTube](https://www.youtube.com/user/ariyahidayat) -Axel Rauschmayer | [Twitter](http://twitter.com/rauschma) | | +Axel Rauschmayer | [Twitter](http://twitter.com/rauschma) | [Blog](http://2ality.com/) | Andrea Giammarchi | [Twitter](http://twitter.com/WebReflection) | [Blog](http://webreflection.blogspot.gr/) | Angus Croll | [Twitter](http://twitter.com/angusTweets) | [Blog](http://javascriptweblog.wordpress.com/) | Béla Varga | [Twitter](https://twitter.com/netzzwerg) | | From 93c146cc91aa193a8dc7e850f0769832fb4e0334 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 19 Jun 2019 23:53:46 +0300 Subject: [PATCH 079/227] Update README.md --- README.md | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8d59485..beec3f4 100755 --- a/README.md +++ b/README.md @@ -11,22 +11,22 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ### LEARNING -* Understand type coercion +* Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#coercion) * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. - Here are 2 great videos on the subject by Paul Irish: - * [10 Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=i_qE1iAmjFg) - * [11 More Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=ARnp9Y8xgR4) + Here are 2 great videos on the subject by Paul Irish: + * [10 Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=i_qE1iAmjFg) + * [11 More Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=ARnp9Y8xgR4) * Watch every video by Douglas Crockford. * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) * Study performance and code optimization - Here is a good article by Nicholas Zakas: - [10 Javascript Performance Boosting Tips](http://jonraasch.com/blog/10-javascript-performance-boosting-tips-from-nicholas-zakas) + Here is a good article by Nicholas Zakas: + * [10 Javascript Performance Boosting Tips](http://jonraasch.com/blog/10-javascript-performance-boosting-tips-from-nicholas-zakas) * Check every tip on the [A Drip of JavaScript](http://designpepper.com/js-drip-archive/) list and subscribe to it. * Check [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/). @@ -41,17 +41,17 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Study JavaScript Patterns and Anti-Patterns: - * [Learning JavaScript Design Patterns, Addy Osmani](http://addyosmani.com/resources/essentialjsdesignpatterns/book/) - * [Javascript Patterns, moderated by *Shi Chuan*](http://shichuan.github.io/javascript-patterns/) + * [Learning JavaScript Design Patterns, Addy Osmani](http://addyosmani.com/resources/essentialjsdesignpatterns/book/) + * [Javascript Patterns, moderated by *Shi Chuan*](http://shichuan.github.io/javascript-patterns/) * Understand the Single Threaded Process & Event Loop. - * [Concurrency model and Event Loop, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop) - * [The JavaScript Event Loop: Explained, By Erin Swenson-Healey](http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) - * [Understanding the node.js event loop, by *Mixu*](http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/) - * [Introduction to Javascript Processes, by *Nico Valencia *](http://quickleft.com/blog/introduction-to-javascript-processes) - * [How JavaScript Timers Work, By John Resig](http://ejohn.org/blog/how-javascript-timers-work/) - * [Philip Roberts: Help, I'm stuck in an event-loop: How JavaScript actually works](http://vimeo.com/96425312) + * [Concurrency model and Event Loop, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop) + * [The JavaScript Event Loop: Explained, By Erin Swenson-Healey](http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) + * [Understanding the node.js event loop, by *Mixu*](http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/) + * [Introduction to Javascript Processes, by *Nico Valencia *](http://quickleft.com/blog/introduction-to-javascript-processes) + * [How JavaScript Timers Work, By John Resig](http://ejohn.org/blog/how-javascript-timers-work/) + * [Philip Roberts: Help, I'm stuck in an event-loop: How JavaScript actually works](http://vimeo.com/96425312) * Study some of the modern APIs: [WebRTC](https://developer.mozilla.org/en-US/docs/Web/Guide/API/WebRTC), [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), [Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob), etc. * Master `Regular Expressions`. @@ -59,16 +59,16 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Subscribe to these great YouTube channels and watch every video you can: * [CascadiaJS](https://www.youtube.com/user/cascadiajs) - * [JSConf](https://www.youtube.com/user/jsconfeu) - -* Learn some framework or library like [jQuery](http://jquery.com/), [Backbone.JS](http://backbonejs.org/), [underscore](http://underscorejs.org/), [Angular](https://angularjs.org/), [Ember](http://emberjs.com/), [Knockout.JS](http://knockoutjs.com/), etc. + * [JSConf](https://www.youtube.com/user/jsconfeu/videos) + +* Learn some framework or library like [jQuery](http://jquery.com/), [React](https://reactjs.org/), [Angular](https://angular.io/), [Vue.JS](https://vuejs.org/), [Backbone.JS](http://backbonejs.org/), [underscore](http://underscorejs.org/), [AngularJS](https://angularjs.org/), [Ember](http://emberjs.com/), [Knockout.JS](http://knockoutjs.com/), etc. * [Try building a ToDo App using one of these libraries](http://todomvc.com/) * Get to know [Node.JS](http://nodejs.org/) and start building apps. * Try running server-side JS code usign `Node.JS` on [Runnable.com](http://runnable.com/) * Install [Node.JS](http://nodejs.org/) locally and start experimenting. * Subscribe to Newsletters and stay up to date with JS and Node. *(See section `3.8 NEWSLETTERS`)* * Become familiar with a Task Manager like [Grunt](http://gruntjs.com/) or [Gulp](#gulp). -* Start building Desktop Applications using HTML/JavaScript in [Node-Webkit](https://github.com/rogerwang/node-webkit). +* Start building cross-platform Desktop Applications using HTML/CSS/JavaScript in [Electron.JS](https://electronjs.org/) or [Node-Webkit](https://nwjs.io/). * Follow some of the masters on Twitter. *(See `section 3.6` of the RESOURCES section)* * Code using a [Style Guide](https://github.com/airbnb/javascript). @@ -354,8 +354,11 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ##### 3.4.5 KNOCKOUT JS -##### 3.4.5 REACT +* [Learn Knockout.JS](http://learn.knockoutjs.com/) + +##### 3.4.5 REACT.JS +* [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) * [Getting Started With React](http://www.tysoncadenhead.com/blog/getting-started-with-react) #### 3.5 BOOKS From cbd0ec1d69c8ed1eaee9f0103d27e38b2c774ecb Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 19 Jun 2019 23:54:41 +0300 Subject: [PATCH 080/227] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index beec3f4..fc79090 100755 --- a/README.md +++ b/README.md @@ -58,14 +58,14 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Get in the habit of testing. **(Crucial!)**. [QUnit](http://qunitjs.com/) is a good starting point. * Subscribe to these great YouTube channels and watch every video you can: - * [CascadiaJS](https://www.youtube.com/user/cascadiajs) - * [JSConf](https://www.youtube.com/user/jsconfeu/videos) + * [CascadiaJS](https://www.youtube.com/user/cascadiajs) + * [JSConf](https://www.youtube.com/user/jsconfeu/videos) * Learn some framework or library like [jQuery](http://jquery.com/), [React](https://reactjs.org/), [Angular](https://angular.io/), [Vue.JS](https://vuejs.org/), [Backbone.JS](http://backbonejs.org/), [underscore](http://underscorejs.org/), [AngularJS](https://angularjs.org/), [Ember](http://emberjs.com/), [Knockout.JS](http://knockoutjs.com/), etc. * [Try building a ToDo App using one of these libraries](http://todomvc.com/) * Get to know [Node.JS](http://nodejs.org/) and start building apps. - * Try running server-side JS code usign `Node.JS` on [Runnable.com](http://runnable.com/) - * Install [Node.JS](http://nodejs.org/) locally and start experimenting. + * Try running server-side JS code usign `Node.JS` on [Runnable.com](http://runnable.com/) + * Install [Node.JS](http://nodejs.org/) locally and start experimenting. * Subscribe to Newsletters and stay up to date with JS and Node. *(See section `3.8 NEWSLETTERS`)* * Become familiar with a Task Manager like [Grunt](http://gruntjs.com/) or [Gulp](#gulp). * Start building cross-platform Desktop Applications using HTML/CSS/JavaScript in [Electron.JS](https://electronjs.org/) or [Node-Webkit](https://nwjs.io/). From ae56c4dbcdcdb882076347d4501cbf645d842c84 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 21 Jun 2019 11:37:30 +0300 Subject: [PATCH 081/227] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc79090..c202a65 100755 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s #### 3.1 BEGINNER RESOURCES +* [**JavaScript First Steps** | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps) * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) From 1590e8b5e95dd6bb25fa98ce3c25847d34da12f8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 21 Jun 2019 11:40:01 +0300 Subject: [PATCH 082/227] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c202a65..1c366b8 100755 --- a/README.md +++ b/README.md @@ -264,6 +264,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > VIDEOS +* [Express.js Tutorial: Build RESTful APIs with Node and Express | Mosh](https://www.youtube.com/watch?v=pKd0Rpw7O48) +* [Node.js Tutorial for Beginners: Learn Node in 1 Hour | Mosh](https://www.youtube.com/watch?v=TlB_eWDSMt4) * [NodeTuts: Node.JS Video Tutorials](http://nodetuts.com/) * [Node.JS videos @ egghead.io](https://egghead.io/technologies/node) From e066457dbf960254d67f65c11f763b48f0cdb641 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 21 Jun 2019 11:42:14 +0300 Subject: [PATCH 083/227] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 1c366b8..28d077f 100755 --- a/README.md +++ b/README.md @@ -515,6 +515,14 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c * [**The Little Book on CoffeeScript**](http://arcturo.github.io/library/coffeescript/) * [**CoffeeScript: The Good Parts**, By Azat Mardan](https://qconnewyork.com/ny2014/system/files/presentation-slides/CoffeeScript-The-Good-Parts.pdf) +##### 3.11.2 TYPESCRIPT + + * [TypeScript](https://www.typescriptlang.org/) + +##### 3.11.3 ELM + + * [Elm](https://elm-lang.org/) + ------ ![Alt text](/img/javascript-please-work.jpg "JavaScript please work") From 0b13f4b385fa981f752062db61e5311b1e7be78f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 21 Jun 2019 11:42:38 +0300 Subject: [PATCH 084/227] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 28d077f..32745cc 100755 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ Contributors: Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. -![Alt text](/img/2017-JS.png) - ### LEARNING * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#coercion) From 4f4c94982b5caf8c7feddcee4d4c607ec62e0b5b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 01:50:41 +0300 Subject: [PATCH 085/227] Updating resources for Async, React, Angular, TypeScript --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 32745cc..175968b 100755 --- a/README.md +++ b/README.md @@ -160,8 +160,13 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A Couple of Quick Tips for JavaScript Optimization](https://mondaybynoon.com/a-couple-of-quick-tips-for-javascript-optimization/) * [CSS TRIGGERS](http://csstriggers.com/) -> TAMING
THE ASYNCHRONOUS NATURE OF JAVASCRIPT +> TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT [ UPDATED ] +* [**Asynchronous JavaScript: From Callback Hell to Async and Await**](https://www.toptal.com/javascript/asynchronous-javascript-async-await-tutorial) +* [**Promises, async/await @ JavaScript.info**](https://javascript.info/async) +* [**Async JS Crash Course - Callbacks, Promises, Async Await** YouTube video by Traversy Media | 25min ](https://www.youtube.com/watch?v=PoRJizFvM7s) +* [**async / await in JavaScript - What, Why and How - Fun Fun Function** YouTube video | 24min](https://www.youtube.com/watch?v=568g8hxJJp4) +* [**Asynchronous JavaScript with async/await**](https://egghead.io/courses/asynchronous-javascript-with-async-await) * [**JavaScript Goes Asynchronous (and It’s Awesome)**](http://www.sitepoint.com/javascript-goes-asynchronous-awesome/) * [**Write Better JavaScript with Promises**, By Landon Schropp](http://davidwalsh.name/write-javascript-promises) * [**The Evolution of Asynchronous JavaScript**](https://blog.risingstack.com/asynchronous-javascript/) @@ -273,10 +278,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s > HOSTING PLATFORMS +* [Heroku](https://www.heroku.com/) * [Modulus](https://modulus.io/) * [Nitrous.IO](https://www.nitrous.io/) * [Cloud9](https://c9.io/) -* [Heroku](https://www.heroku.com/) * [OpenShift](https://www.openshift.com/) * [NodeJitsu](https://www.nodejitsu.com/) * [NodeGear](https://nodegear.com/) @@ -320,9 +325,13 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [A tidy repository of jQuery plugins, *by @psnka*](http://unheap.com/) -##### 3.4.3 ANGULAR JS +##### 3.4.3 ANGULAR & ANGULAR JS [ UPDATED ] + +> TUTORIALS: ANGULAR -> TUTORIALS +* [**Angular Tutorial for Beginners: Learn Angular from Scratch** | Mosh | YouTube video | 2h](https://www.youtube.com/watch?v=k5E2AVpwsko) + +> TUTORIALS: ANGULARJS * [3 Reasons to Choose AngularJS for Your Next Project](http://code.tutsplus.com/tutorials/3-reasons-to-choose-angularjs-for-your-next-project--net-28457) * [Comprehensive Beginner’s Guide to AngularJS](http://antjanus.com/blog/web-development-tutorials/front-end-development/comprehensive-beginner-guide-angularjs/) @@ -339,7 +348,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Speeding up AngularJS apps with simple optimizations](http://www.binpress.com/tutorial/speeding-up-angular-js-with-simple-optimizations/135) * [Optimizing AngularJS: 1200ms to 35ms](http://blog.scalyr.com/2013/10/angularjs-1200ms-to-35ms/) -> VIDEOS +> VIDEOS: ANGULARJS * [AngularJS Fundamentals In 60-ish Minutes, *by Dan Wahlin*](https://www.youtube.com/watch?v=i9MHigUZKEM) * [AngularJS videos @ egghead.io](https://egghead.io/technologies/angularjs) @@ -357,8 +366,10 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Learn Knockout.JS](http://learn.knockoutjs.com/) -##### 3.4.5 REACT.JS +##### 3.4.5 REACT.JS [ UPDATED ] +* [**React Tutorial - Learn React - React Crash Course [2019]** YouTube video | 2h 25min ](https://www.youtube.com/watch?v=Ke90Tje7VS0) +* [**Introduction to React** YouTube video by Le Wagon | 1h 53min](https://www.youtube.com/watch?v=_ZTT9kw3PIE) * [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) * [Getting Started With React](http://www.tysoncadenhead.com/blog/getting-started-with-react) @@ -516,6 +527,8 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c ##### 3.11.2 TYPESCRIPT * [TypeScript](https://www.typescriptlang.org/) + * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) + * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) ##### 3.11.3 ELM From e0415c95262256aec55f101328060655cf6e433f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 01:51:29 +0300 Subject: [PATCH 086/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 175968b..06028c9 100755 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Always use `let` or `const` when declaring variables. Avoid using `var`. * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) -* [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonley Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) +* [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonely Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) * [Prefer using `===` over `==`](http://bonsaiden.github.io/JavaScript-Garden/#types.equality) * Always use curly braces * Comment your code From 58a65a8a56e174eeadc30cc86a69b78cfb6906f2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 01:58:42 +0300 Subject: [PATCH 087/227] Updating ES6 section --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 06028c9..bdc6ec4 100755 --- a/README.md +++ b/README.md @@ -89,15 +89,21 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") -#### ECMASCRIPT 6 | ES2015 - -* [**ES6 Overview in 350 Bullet Points**](https://ponyfoo.com/articles/es6) -* [**Overview of ECMAScript 6 features**](https://github.com/lukehoban/es6features) +
+ +

ECMASCRIPT 6 | ES2015 [ UPDATED ]

+

+

Learn modern JavaScript | Scrimba Interactive Screencasts | 54min

+

Introduction to ES6+ | Scrimba Interactive Screencasts | 67min

+

ES6 Overview in 350 Bullet Points

+

Overview of ECMAScript 6 features

+
### 3. RESOURCES -#### 3.1 BEGINNER RESOURCES +#### 3.1 BEGINNER RESOURCES [ UPDATED ] +* [**Introduction to JavaScript** | Scrimba Interactive Screencasts | 69min ](https://scrimba.com/g/gintrotojavascript) * [**JavaScript First Steps** | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps) * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) From ee83a44c00b361f0b2aa65fb47382830c7c558f1 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 02:00:22 +0300 Subject: [PATCH 088/227] Update README.md --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bdc6ec4..98ce2d5 100755 --- a/README.md +++ b/README.md @@ -89,14 +89,11 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") -
- -

ECMASCRIPT 6 | ES2015 [ UPDATED ]

-

-

Learn modern JavaScript | Scrimba Interactive Screencasts | 54min

-

Introduction to ES6+ | Scrimba Interactive Screencasts | 67min

-

ES6 Overview in 350 Bullet Points

-

Overview of ECMAScript 6 features

+

ECMASCRIPT 6 | ES2015 [ UPDATED ]

+

Learn modern JavaScript | Scrimba Interactive Screencasts | 54min

+

Introduction to ES6+ | Scrimba Interactive Screencasts | 67min

+

ES6 Overview in 350 Bullet Points

+

Overview of ECMAScript 6 features

### 3. RESOURCES From 4d90f1c3e4c29ef7f199d58ce84b717492beefa7 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 02:05:14 +0300 Subject: [PATCH 089/227] Update README.md --- README.md | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 98ce2d5..7e43430 100755 --- a/README.md +++ b/README.md @@ -89,11 +89,12 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") -

ECMASCRIPT 6 | ES2015 [ UPDATED ]

-

Learn modern JavaScript | Scrimba Interactive Screencasts | 54min

-

Introduction to ES6+ | Scrimba Interactive Screencasts | 67min

-

ES6 Overview in 350 Bullet Points

-

Overview of ECMAScript 6 features

+
+ ECMASCRIPT 6 | ES2015 [ UPDATED ] +

Learn modern JavaScript | Scrimba Interactive Screencasts | 54min

+

Introduction to ES6+ | Scrimba Interactive Screencasts | 67min

+

ES6 Overview in 350 Bullet Points

+

Overview of ECMAScript 6 features

### 3. RESOURCES @@ -324,9 +325,11 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Backbone.js Tutorial for Beginners, by *Thomas Davis*](https://www.youtube.com/watch?v=FZSjvWtUxYk) * [**Jeremy Ashkenas - Taking JavaScript Seriously with Backbone.js**](https://www.youtube.com/watch?v=4udR30JYenA) -##### 3.4.2 JQUERY +
+ 3.4.2 JQUERY +

A tidy repository of jQuery plugins, by @psnka

+
-* [A tidy repository of jQuery plugins, *by @psnka*](http://unheap.com/) ##### 3.4.3 ANGULAR & ANGULAR JS [ UPDATED ] @@ -361,13 +364,19 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [ng-newsletter: The free, weekly newsletter of the best AngularJS content on the web](http://www.ng-newsletter.com/) -##### 3.4.4 EMBER - -* [Ember.js introduction by Kasper Tidemann](https://www.youtube.com/watch?v=L3sYt8PPHyI) [**VIDEO**] - -##### 3.4.5 KNOCKOUT JS - -* [Learn Knockout.JS](http://learn.knockoutjs.com/) +
+ + 3.4.4 EMBER + +

Ember.js introduction by Kasper Tidemann [VIDEO]

+
+ +
+ + 3.4.5 KNOCKOUT JS + +

Learn Knockout.JS

+
##### 3.4.5 REACT.JS [ UPDATED ] From 53699af176d5dc27489bd60a725e25834352df1d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 02:07:35 +0300 Subject: [PATCH 090/227] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7e43430..5e9a537 100755 --- a/README.md +++ b/README.md @@ -536,15 +536,16 @@ Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.c * [**The Little Book on CoffeeScript**](http://arcturo.github.io/library/coffeescript/) * [**CoffeeScript: The Good Parts**, By Azat Mardan](https://qconnewyork.com/ny2014/system/files/presentation-slides/CoffeeScript-The-Good-Parts.pdf) -##### 3.11.2 TYPESCRIPT +##### 3.11.2 TYPESCRIPT [ UPDATED ] * [TypeScript](https://www.typescriptlang.org/) * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) -##### 3.11.3 ELM - - * [Elm](https://elm-lang.org/) +
+ 3.11.3 ELM +

Elm

+
------ From 9eaec3738f8fbe16758573ad40beb90204c81010 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:32:18 +0300 Subject: [PATCH 091/227] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 5e9a537..f2660a9 100755 --- a/README.md +++ b/README.md @@ -188,6 +188,14 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [ESLINT Linter](http://eslint.org/) * [JSCS](https://www.npmjs.org/package/jscs) +
+ > REGULAR EXPRESSIONS [ UPDATED ]Regular Expressions in JavaScript - #1 REGEX ULTRA BASICS | YouTube Video | 23min

+

Regular Expressions - Enough to be Dangerous | YouTube Video | 15min

+

Learn Regular Expressions (Regex) - Crash Course for Beginners @freeCodeCamp | YouTube Video | 45min

+

Learn Regular Expressions | Interactive Screencases @ Scrimba | 48min

+
+ > ADVANCED * [JavaScript language advanced Tips & Tricks](https://code.google.com/p/jslibs/wiki/JavascriptTips) From 51c99106385adb41c2d6422ffaa614e9288e9552 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:34:25 +0300 Subject: [PATCH 092/227] Updating Linters, Regular Expressions and Debugging sections --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f2660a9..ba649c9 100755 --- a/README.md +++ b/README.md @@ -176,20 +176,26 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**The Evolution of Asynchronous JavaScript**](https://blog.risingstack.com/asynchronous-javascript/) * [**Javascript Async Control Flow**, By KENNY KAYE | 20 OCTOBER 2015](https://kaye.us/javascript-async-control-flow/) -> DEBUGGING - -* [Debugging JavaScript](https://developer.chrome.com/devtools/docs/javascript-debugging) -* [Chrome DevTools Command Line API Reference](https://developer.chrome.com/devtools/docs/commandline-api) - -> LINTERS +
+ DEBUGGING +
  • Debugging JavaScript
  • +
  • Chrome DevTools Command Line API Reference
  • + +
    -* [JSLINT](http://www.jslint.com/) -* [JSHINT Linter](http://www.jshint.com/) -* [ESLINT Linter](http://eslint.org/) -* [JSCS](https://www.npmjs.org/package/jscs) +
    + LINTERS +
  • JSLINT
  • +
  • JSHINT Linter
  • +
  • ESLINT Linter
  • +
  • JSCS
  • + +
    - > REGULAR EXPRESSIONS [ UPDATED ]REGULAR EXPRESSIONS [ UPDATED ]Regular Expressions in JavaScript - #1 REGEX ULTRA BASICS | YouTube Video | 23min

    Regular Expressions - Enough to be Dangerous | YouTube Video | 15min

    Learn Regular Expressions (Regex) - Crash Course for Beginners @freeCodeCamp | YouTube Video | 45min

    From ea957719492448ade476c2021b9a282db5b37c9b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:36:25 +0300 Subject: [PATCH 093/227] Updating Developers section --- README.md | 327 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 279 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index ba649c9..b042ac9 100755 --- a/README.md +++ b/README.md @@ -459,54 +459,285 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Node Weekly](http://nodeweekly.com/) * [ng-newsletter: The free, weekly newsletter of the best AngularJS content on the web](http://www.ng-newsletter.com/) -#### 3.9 DEVELOPERS (TWITTER, BLOGS) - -DEVELOPER | TWITTER | BLOG | VIDEO CHANNEL -------------- | ------------- | ------------- | ------------- -Addy Osmani | [Twitter](http://twitter.com/addyosmani) | [Blog](http://addyosmani.com/blog/) | -Azat Mardan | [Twitter](https://twitter.com/azat_co) | | -Alex Castrounis | [Twitter](https://twitter.com/innoarchitech) | [Blog](http://www.innoarchitech.com/) | -Aria Stewart | [Twitter](https://twitter.com/aredridel) | [Blog](http://dinhe.net/~aredridel/) | -Ariya Hidayat | [Twitter](http://twitter.com/ariyahidayat) | [Blog](http://ariya.ofilabs.com/) | [YouTube](https://www.youtube.com/user/ariyahidayat) -Axel Rauschmayer | [Twitter](http://twitter.com/rauschma) | [Blog](http://2ality.com/) | -Andrea Giammarchi | [Twitter](http://twitter.com/WebReflection) | [Blog](http://webreflection.blogspot.gr/) | -Angus Croll | [Twitter](http://twitter.com/angusTweets) | [Blog](http://javascriptweblog.wordpress.com/) | -Béla Varga | [Twitter](https://twitter.com/netzzwerg) | | -Ben Alman | [Twitter](https://twitter.com/cowboy) | [Blog](http://benalman.com/) | -Ben Cherry | [Twitter](https://twitter.com/bcherry) | [Blog](http://www.adequatelygood.com/) | -Brendan Eich | [Twitter](http://twitter.com/BrendanEich) | [Blog](http://brendaneich.com/) | -Christian Heilmann | [Twitter](http://twitter.com/codepo8) | [Blog](http://christianheilmann.com/) | -Cody Lindley | [Twitter](https://twitter.com/codylindley) | [Blog](http://blog.codylindley.com/) | -David Flanagan | [Twitter](http://twitter.com/__DavidFlanagan) | [Blog](http://davidflanagan.com/) | -David Walsh | [Twitter](http://twitter.com/davidwalshblog) | [Blog](http://davidwalsh.name/) | -Dion Almaer | [Twitter](https://twitter.com/dalmaer) | [Blog](http://almaer.com/blog/) | -Douglas Crockford | | [Blog](http://javascript.crockford.com/) | -Dean Edwards | | [Blog](http://dean.edwards.name/weblog/) | -Dmitry Baranovskiy | [Twitter](https://twitter.com/DmitryBaranovsk) | [Blog](http://dmitry.baranovskiy.com/) | -Dustin Diaz | [Twitter](https://twitter.com/ded) | [Blog](http://www.dustindiaz.com/) | -Eric Elliott | [Twitter](https://twitter.com/_ericelliott) | | -Ilya Grigorik | [Twitter](http://twitter.com/igrigorik) | | -Jeremy Ashkenas | [Twitter](http://twitter.com/jashkenas) | [Blog]() | -John Resig | [Twitter](http://twitter.com/jeresig) | [Blog](http://ejohn.org/category/blog/) | -Juriy Zaytsev | [Twitter](https://twitter.com/kangax) | [Blog](http://perfectionkills.com/) | -James Coglan | [Twitter](https://twitter.com/jcoglan) | [Blog](https://blog.jcoglan.com/) | -John-David Dalton | [Twitter](https://twitter.com/jdalton) | [Blog](http://allyoucanleet.com/) | -James Padolsey | [Twitter](https://twitter.com/padolsey) | [Blog](http://james.padolsey.com/) | -Kyle Simpson | [Twitter](https://twitter.com/getify) | [Blog](http://getify.me/) | -Lucas Smith | [Twitter](https://twitter.com/ls_n) | | -Michael Bolin | [Twitter](https://twitter.com/bolinfest) | [Blog](http://blog.bolinfest.com/) | -Nicholas Zakas | [Twitter](http://twitter.com/slicknet) | [Blog](http://www.nczonline.net/) | -Pamela Fox | [Twitter](https://twitter.com/pamelafox) | [Blog](http://blog.pamelafox.org/) | -Paul Irish | [Twitter](http://twitter.com/paul_irish) | [Blog](http://paulirish.com/) | -Peter van der Zee | [Twitter](https://twitter.com/kuvos) | [Blog](http://qfox.nl/weblog) | -Peter Michaux | | [Blog](http://peter.michaux.ca/) | -Rebecca Murphey | [Twitter](http://twitter.com/rmurphey) | [Blog](http://rmurphey.com/) | -Remy Sharp | [Twitter](http://twitter.com/rem) | [Blog](http://remysharp.com/) | -Rey Bango | [Twitter](https://twitter.com/reybango) | [Blog](http://blog.reybango.com/) | -Stoyan Stefanov | [Twitter](Stoyan Stefanov) | [Blog](http://www.phpied.com/) | -Thomas Fuchs | [Twitter](https://twitter.com/thomasfuchs) | [Blog](http://mir.aculo.us/) | -Oliver Steele | [Twitter](https://twitter.com/osteele) | [Blog](http://www.osteele.com/) | -Yehuda Katz | [Twitter](https://twitter.com/wycats) | [Blog](http://yehudakatz.com/) | +
    + 3.9 DEVELOPERS (TWITTER, BLOGS) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DEVELOPERTWITTERBLOGVIDEO CHANNEL
    Addy OsmaniTwitterBlog
    Azat MardanTwitter
    Alex CastrounisTwitterBlog
    Aria StewartTwitterBlog
    Ariya HidayatTwitterBlogYouTube
    Axel RauschmayerTwitterBlog
    Andrea GiammarchiTwitterBlog
    Angus CrollTwitterBlog
    Béla VargaTwitter
    Ben AlmanTwitterBlog
    Ben CherryTwitterBlog
    Brendan EichTwitterBlog
    Christian HeilmannTwitterBlog
    Cody LindleyTwitterBlog
    David FlanaganTwitterBlog
    David WalshTwitterBlog
    Dion AlmaerTwitterBlog
    Douglas CrockfordBlog
    Dean EdwardsBlog
    Dmitry BaranovskiyTwitterBlog
    Dustin DiazTwitterBlog
    Eric ElliottTwitter
    Ilya GrigorikTwitter
    Jeremy AshkenasTwitterBlog
    John ResigTwitterBlog
    Juriy ZaytsevTwitterBlog
    James CoglanTwitterBlog
    John-David DaltonTwitterBlog
    James PadolseyTwitterBlog
    Kyle SimpsonTwitterBlog
    Lucas SmithTwitter
    Michael BolinTwitterBlog
    Nicholas ZakasTwitterBlog
    Pamela FoxTwitterBlog
    Paul IrishTwitterBlog
    Peter van der ZeeTwitterBlog
    Peter MichauxBlog
    Rebecca MurpheyTwitterBlog
    Remy SharpTwitterBlog
    Rey BangoTwitterBlog
    Stoyan Stefanov[Twitter](Stoyan Stefanov)Blog
    Thomas FuchsTwitterBlog
    Oliver SteeleTwitterBlog
    Yehuda KatzTwitterBlog
    +
    #### 3.10 TOOLS From 1b00a03096839856578a552ec30a94d1b3a0583e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:39:40 +0300 Subject: [PATCH 094/227] Adding Software principles tips --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b042ac9..b4ca002 100755 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Use [JSHint](http://www.jshint.com/) / [JSLint](http://www.jslint.com/) * [Inline lint highlighting for the Sublime Text 2 editor](https://github.com/SublimeLinter/SublimeLinter-for-ST2) * [Interactive code linting framework for Sublime Text 3](https://github.com/SublimeLinter/SublimeLinter3) + * [Study and apply Software Principles such as **DRY**, **SOLID**, **KISS**, etc.](https://dev.to/pluralsight/my-5-favorite-software-design-principles-4ech) *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") From 4320ba8e36d67b788c1fd197a9f8e925d9f8587a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:43:07 +0300 Subject: [PATCH 095/227] Fixing broken link to Type Coercion --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4ca002..a160f8c 100755 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ### LEARNING -* Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#coercion) +* Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) From 793751427e598623269a09953a2e8fdff30a1723 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Sep 2019 22:46:35 +0300 Subject: [PATCH 096/227] Adding anchor link for RegEx --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a160f8c..9ad6f13 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [Philip Roberts: Help, I'm stuck in an event-loop: How JavaScript actually works](http://vimeo.com/96425312) * Study some of the modern APIs: [WebRTC](https://developer.mozilla.org/en-US/docs/Web/Guide/API/WebRTC), [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), [Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob), etc. -* Master `Regular Expressions`. +* [Master `Regular Expressions`](#regex). * Get in the habit of testing. **(Crucial!)**. [QUnit](http://qunitjs.com/) is a good starting point. * Subscribe to these great YouTube channels and watch every video you can: @@ -195,7 +195,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s
    -
    +
    REGULAR EXPRESSIONS [ UPDATED ]Regular Expressions in JavaScript - #1 REGEX ULTRA BASICS | YouTube Video | 23min

    Regular Expressions - Enough to be Dangerous | YouTube Video | 15min

    From b97cc8dc0fb206ca08e3c2b14533bc1935fdd2dc Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Dec 2019 23:29:48 +0200 Subject: [PATCH 097/227] Adding the amazing Lydia Hallie and her awesome posts! --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9ad6f13..1ae15fe 100755 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) + + Here are two really good posts explaining scope and hoisting: + * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By Lydia Hallie + * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By Lydia Hallie + * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. @@ -659,6 +664,12 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s + Lydia Hallie + Twitter + Blog + YouTube + + Michael Bolin Twitter Blog From 71a6b9f5b11d1c1f0eccda04130db2cd97863cce Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 17 Dec 2019 23:31:59 +0200 Subject: [PATCH 098/227] Updating links... --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1ae15fe..298c665 100755 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) Here are two really good posts explaining scope and hoisting: - * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By Lydia Hallie - * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By Lydia Hallie + * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By [Lydia Hallie](https://github.com/lydiahallie) + * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) @@ -665,8 +665,8 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s Lydia Hallie - Twitter - Blog + Twitter + Blog YouTube From c654bc1e74cd755fa39fdae3201aa27f47f92720 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Dec 2019 17:55:10 +0200 Subject: [PATCH 099/227] Updating ReactJS links --- README.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 298c665..5bf74d5 100755 --- a/README.md +++ b/README.md @@ -3,10 +3,6 @@ LearnJavascript: A list of resources This list originated from this [discussion](http://www.linkedin.com/groupItem?view=&gid=121615&item=5909742634301140992&type=member&commentID=5909826380652113920&trk=eml-ntf-hero-like-my-discussion-cmt&midToken=AQG2dgaKId8xLA&fromEmail=fromEmail&ut=2sQj9Dud2coCo1#commentID_5909826380652113920) on LinkedIn's Javascript group. -Contributors: - -Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. - ### LEARNING * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) @@ -404,6 +400,30 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s * [**Introduction to React** YouTube video by Le Wagon | 1h 53min](https://www.youtube.com/watch?v=_ZTT9kw3PIE) * [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) * [Getting Started With React](http://www.tysoncadenhead.com/blog/getting-started-with-react) +* [**Started with React** - An Overview and Walkthrough](https://www.taniarascia.com/getting-started-with-react/) +* [Thinking in React](https://reactjs.org/docs/thinking-in-react.html) +* [(YouTube) Learn React - React Crash Course [2019] - React Tutorial with Examples | Mosh](https://www.youtube.com/watch?v=Ke90Tje7VS0) +* [How to Learn React — A roadmap from beginner to advanced (2018)](https://www.freecodecamp.org/news/learning-react-roadmap-from-scratch-to-advanced-bff7735531b6/) +* [React 101 - Learn how to build 3 practical React components from scratch!](https://ihatetomatoes.net/get-react-101/) +* [Learn React for free](https://scrimba.com/g/glearnreact) +* [Learn React in 10 tweets (with hooks)](https://twitter.com/chrisachard/status/1175022111758442497) +* [Introduction to React by Le Wagon [Video]](https://www.youtube.com/watch?v=_ZTT9kw3PIE) +* [React.JS @ Reddit](https://www.reddit.com/r/reactjs) +* [What I wish I knew when I started to work with React.js](https://medium.freecodecamp.org/what-i-wish-i-knew-when-i-started-to-work-with-react-js-3ba36107fd13) +* [Create React App: How to Update to New Versions?](https://create-react-app.dev/docs/updating-to-new-releases/) +* [React Routing: Quick Start](https://reacttraining.com/react-router/web/guides/quick-start) +* [React Routing: Difference between exact path and route path](https://stackoverflow.com/questions/49162311/react-difference-between-route-exact-path-and-route-path) +* [React Routing: A Bluffer's Information to React Router V4 – FreeCodeCamp](https://epeak.info/2019/03/26/a-bluffers-information-to-react-router-v4-freecodecamp-org/) +* [React Hooks: Thinking in React Hooks](https://wattenberger.com/blog/react-hooks) +* [State Management: Using the native Context API (React 16.3+)](https://reactjs.org/docs/context.html) +* [State Management: What is the Context API? (NetNinja Video)](https://www.youtube.com/watch?v=XkBB3pPY3t8) +* [State Management: Introducing the React Context API - YouTube](https://www.youtube.com/watch?v=yzQ_XulhQFw) +* [State Management: Simple example using Context API](https://codesandbox.io/s/reactjs-context-api-peflc) +* [State Management: The PubSub Pattern](http://robertmarkbramprogrammer.blogspot.com/2019/02/using-pubsubjs-in-react.html) +* [State Management: The PubSub Pattern: Demo](https://codesandbox.io/s/reactjs-pubsub-fjwdj) +* [State Management: Using Flux for unidirectional data flow](https://facebook.github.io/flux/) +* [State Management: Redux](https://react-redux.js.org/) +* [State Management: MobX](https://mobx.js.org/) #### 3.5 BOOKS @@ -807,3 +827,7 @@ Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for s ------ ![Alt text](/img/javascript-please-work.jpg "JavaScript please work") + +#### Contributors + +Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. From 7c232230a56ab7a7bc15d81d4c8e687ccd5a9465 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Dec 2019 17:57:34 +0200 Subject: [PATCH 100/227] Add files via upload --- img/When-in-Rome.jpg | Bin 0 -> 127475 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/When-in-Rome.jpg diff --git a/img/When-in-Rome.jpg b/img/When-in-Rome.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b648f32481da55569e51aee9df0985159be9b9a0 GIT binary patch literal 127475 zcmb5VWmKC>*fo3yhXTb*vEoo%f=h#YaMwbS;1ImPK}vBaMT0|dihE0;xO;IY1Su`j z9%y^coAW&Hde`^&yRz>5ncTA`*P7XL&E9+d{qpY~K%uR!r4E2V006>%fPXgu6@Y}0 zh?t0ggqVn!^brZ^V+P8{gaIx`lbFy)AJQtD^doCa?z`-f54whDUp{%0J zE2^od3DJ{NQhxDYC!j~9q>srSvrtm9yx`;Hd-4A}{v84+iEw%FOL0Mz01hPxmlE{v zXMhPiPJGaR!~K5&!okJECmD8NRpG^Nya3AnJTN)uR(0v;A9sfeDceR+2^|W=Rv0ixdJd1#J>1o}(zP zz0~e_mKh=o#joDvrQ{=(T$WvLEf$B%W%|CobDKQ!m-OxNEp%2bC`I#jm#X5BJ#B#X2SzBn(WeaSs zq+IFIk!!j^zZk~C^0Vx~;>nBj(31zVL#Vxi>{ghh+M)-!t5`{qNred`GL?zAKeI-8 zPeC#c!GbExYhrJX?p~fonTvVr5+f|)gT)|*th~~kBxyD)8U3l2f?}#Nl|;(#jY&Kk zwn$Sb7(RnaX#g{z+zf~+Yc9O1^vu*<^57{gt90S%OGf7vtkk7Bx*u>AZl_AEkG;fr z!ae2!1*d;cZi|lx!kk!Y`Lp%ze|Qd=JCErteXboAp`{Jb+P|Y1~ z^X9mv;{K5?{pJ#7DO7H4b4i&#l;8G+)N`h@4Wlaj&62W3k%KemjvOeBsoB_?$1GX@ z%ufX6ym=||{j@tpFHfA4UW)9MIb^|# zHf%t1&7$bTWA%Rl$!?^a*;0AuTHM-^RdUwfKRM#{Wzg>R0fK31baPPslDie*q>~SN z8hp=PD)c-Z#7W!7Dk^Wc$wD5^-|s$GEgue9{C z&9&I7HGVbt_3xpiOUQG^Ft0~7ophNE$rIa;8ewdMrKCDzNZ!lg_F{*-oKv==$@rG? z1YfcDrmsLm8=vWkhyZp1fYl9v@bUu&R!rntGrYi_$q%-lBJAwO!^^jlt7zg}!tqw~ z%C%y7#U!TK0&qM1RwOd3^XnI!L z9Nx4x_U4_Kh@s{Lj)^n_vb39qVb*<^lXxi;tgjlBvAjaXQB;$E^0NR#$K6Z6l-USY zJB2^zpSgo#CYHZl4-^TNpG_1`oTr_d&n?VPEx-M{F);k|m7;eLV&%=@Zi%Z++s$H{ zoE3AlkttG6p0%MlODszm|L7*)LYRHF&8j&4S;T%l>)CbifDf|$@J0I4=l$FEiIJ&_ zij!NOrpPu=VQ~hJWazNDikbq@PN%FS4?rLg2X%@#-v%MBmD*so>0Ajb*W5+uOQfXf zD}fZZPedE6SgbX`iQ=LsApxQc41D>S$Q=`h4W*e#$jL$?8WK5Sek+W3rnhO4$rs1e zZ6+H<-LG5u!lZ{8r9Pi{6ut4rZIb8dvem%Vhhbj z-5#FWwwAoupsrwT5NOunOA2d6wYnemuY?agx)1xl%Us-?+kSn5az(&0z246D7^aGU z+t}c7RUeJP5E+eB+iur0 z%IFcVG>Z(ixzHMy+nknTapF@xZemN`s#hr?9Q;$=X$8iwDsu$hCCSc7c^EPkKGSon zLIlpg?5j{H40E!RHS7KE9)>zRJ}aMi&}TZ_M?wXCW^HmkZ;CtnkpD>7$kzJ(Du*`U z@DRjtd!ZOj3i8B;ZNQlc7^`{r_Dw?{9+oA-3Z%J(J?D0(t3dKZk*g+lbE(Byi7Xo$^&3}I zuey^Q*Pr=v=exr1F5m$kXrshEF^ahS!zwAuoOqkpXA#mf?~RRF4?2*o=vp_9oaz)f z=fvG2{8khegp|2}V}^Uq9}7z!cOyqBlT+GaJ>K|7w9P$O-To7wsKYXC_d@;U7pfTi zWewG6UZ;-A1P!8F{L<;njoJk@tsZZ|WIB(ss)=NKm>3As!{)Ki=+^MJiNcF zGB8D{V`8^sYG5Z_F7aG<32f{xUJ!KZ-+v7kH0?!Tp6m8|>fTDjR`(41Z^9fy2IRw- zvnE7q&+oru=tJ&a2nLJvQJY$g`zUO==hYA~FyzTf+f}-J5!1}zX%jb{=?IZAyJf6* z*Y&85B}pF9o)&M_J{{@*SyE!P8mSXp%z2W^A}AnpB*&sr(X6wAhX3A4$;0>=YgNE; zS!C3JujOa(v<*YVD`N=@xXA{LR(-icTI;eSfj;5Ps(Ln>zq93OHf^HN7UR%|H1@Pu z-3yPLOZZcaQ(?7}u1b^p&>uK=L|AB8*E7Yme^}}#+Jb3eV)G5HkdPvPH-ll6E&36E zc`F}#%{hDbo~nw2L=>)JR183YU7RTpHy|nsAX>zx4F?1;0E03iUy+B!-qLKxS(R?I}WOxYLU1#iAqZ9I8gu&gdm1N z6~G~*2RH!=07Cy?ny`?@_cY$NV3<)k*%$r;^Rnae_l{I;b>3(-40$no$oOfK=d{OG zuAr~pC|+Oj0=?|-v2aBN&56cHxDGk@L_QaV>Cw|qDr_Huu*inW2m@7RnF*VIxKb~<2zC5jJ4QpktlRmmLaXo12yy@+0$EufObvj+k^#Z*!-R&fw5GSO9i1KePulpwqw2g~ zqMtn*R!tCA4g4wQQkOQQ`)k^Cu6oWYmuz9wh02*I+-L|vVyr{Csz5_httqpK8MEJ- zs(_;x9z6h*mKy+0(Mn1Xe1ISdL;-*S3W7IeF-kEIga0{#fuf2e2yvrNlgW-lT`_^S z&hQ`BW7gX&!CVWzm^osxs`- z=lG*J15C?TLV3@E=BwSY73hw4lRlaPGP%ZkQff>ZHA$l!*T`VN<(!Lg;wU!ZiO_dM zwP~}NtGzOTwwhWuCG8V^!QwP1#%&RZasW!%l$4ZIE9gtX*xADiMB$YFm$4WFh+*kl zfXs+~&mcseC6WGHh%{{y34PJ#lqDKtYU_OSV`JFbR?+qOg0CHgOcpdF?;SJZLp$L0!6;m!;-4QVaCxlB9es}(p!a9eyuedb1$+;VT9cR|(Y_;ek# zn)~PTLh}BKzPb;aY8kV|uhh8_1NYe7=e|lu?Kds`vPSHm*a|e;fio-Ulp-j5%^No-9>Px{oLoe<=)h3H;GybL)!SmZ$8$ zxn983F@1(m;i}*23DHSZJ<;c1Ce0V-EO^-#ICYgvvXf+pT8gvfNl#i};+@Z(w;bQ^ zuUf&R#p||t1(Ybns||y1CkFg9D)hxJFVkw6l$G3O3+}xzU(S1r4vLI=;Lyqg2>nUX zVtXSmju|PvDDcX&TE4Som|HX3qmbOkXs$fgQa6M5D1e?^n-yXJr_mk(C6NHwc>zQL z1f|#`N@*!T0!RY}Af>_DZWeN4&~%8rzEiY%DKX%XCHfz0@m~c%&;lxrJ&}3%Y9=FV z5~nn^B55|;ER~Bftz*bZytROBJImXfgBb+)zxMZvpL1GR#cjZ03&#SJr_LXq>3iGT zI~U!{+%VgAU-pL<>%fBo!A1t|C=9=5wrv}=~&3zm-?spGx`W&WEbY*Yi(AdI%Q@v{SbKPS_rF+7X zjU1->kW1u1vQ%J(hy@t1Ndbt6w)Bih=O+rrpuPDvmW!~Bx)l*i|Ci?e@7i%<3k(3q zC=vjBt^fd>>eA8*1CtymW5)hKIx!9;r1&vm>a>9Y0|-PP^}j(3DvgT5!3IF)q&~0e z@Up+_X@Q4qojlU`=-}c;CgOyToqAyze!l6-#A7edAmDyJAZO4JhwOhYFZ8joK1}=QG@+{1Yk2hdlMHa8Z_Fwy(Yfs%h1){{1zj+rPOi z;atsF^8j0@f0isCuDeR9`hW@sTX5DxmFqiXS3l3PJCwODGYIP^$u{qU^ ztS(xbfJ0%kHj6RsqRWU<0C=e^YEjzAItX!7r^RIrU+({+W9VctTZzkE?;J+&DqS?< zz~vYf=Rm?rg9Ve)J%2BLuAtT8YQey(xt*n1-%3-lF1BeCGAy)+Xfghml?3$+3JNzO zBJBP|K|z@Su(DGokP*ZZaPW|MtRt)6abMr}HaZyw-OI1u&_hNvoaVap>Ayu-Fgz8m zKEkNC8F3ff+F7s9i1Syx`=VgG9)S!9{C+(vh$*@=6~Pp^5dxxAskPFAJnp6$ork4< zE32NlhO^GPNWw?e;ajj!lC+k(TzR`NJ3Zs2Tv;z$t6I0b3*Pprzo!o(`U~axo$U&6 zPzz3-U&DU-RZnuMv(uXf2t6Ap@jV;q%WMB{%TvZ> zmF88(l3U;Gz-yWN&{V!fDc&wbYJ?P;%T2pP4)FBVLK%R z@7v&*FU2E??{l0|ZDS>u)Db|Hk2B5~_^FUkY080|#KEt@rum+WO+tPJOD$cumP%=TY~+vE;& z+7;P)O1^_Vl{Jg=6BL=JOE!;lp1#P?ESz|iV}-sg+Me}#F5Y)-rC$xB&o@iZrHe7h zOoM!;aZF*Obr?h){W4~7a7Uwz(+0T1?&=!eoFXUN(ZZG2HuJ1>QFIw`j_o2ZA-GJ7 zEvhl}xW?{&Y4tL<{?89;cY1_G{X;F5zg@70gv!yEI{D(ahDG&$p9VJpu1lUEB3f(_?fLe8XPX-rm8+a?|d?i@gOS_Oz?k zv{89n4ApJW0*fjn-SZBzi-R;X-#p_ldq`^0y?s)MgJ?}w1Ff0`Kl^*gI4Ct`(&yc3 z-WY1-WwTl_(sQS^)=)&>piu!O6M`tE%K!w~9%(^vqGJhUs$i-j6l4szrpm=~GGKV3 zb?rM;!T1=rx1ZDphu?8K$6rq-|cel0mMPo0L7v?81LH9+oeYA1;lQ?}l z5iTP84~`7oEM3d14GXsY^^5NZOhsHh3u0|~=DVbOo@4r+C(<$1aXvJCexhiayB*gUBF6^0B5GS#IA=gweScNG$6HEpYz zrOwGY0*p+l^I@k_RA*{R&`-v z`9>$M_M&Ms5%1T~jVuvABTB@dFf_mY-q=FadK3BO=bsQ6kCS5awYFzUrqD5${r&KouxjL=c>(p0%21c;( zF4jRETAf|UAH<&X`zO*5dI+#X%tW+_M2RM&88sCnulUZ(XMg%FX?uiuM3}xgA-4HY zb(^C}4zn_oR?!|aG9;ylBCQm)XeX0a0kHx&xEfR;pFU3wsy!qYUAHQqs5jYo@;K{Rh`qKjEfJiXQTz4Ig4%_5MEo0NqvGm>t42HVXz**12(j zz?}JaHpLrNk3_B7EOf6GTvEr$7=WzQy>Gq&mIJ)?wUybfjal}g{Y-IBS!=m6z0HpO z^|ZiuMm+5PS}*BxHXEu#`w>*JXZDr#5aLo&1J!lGOx>hJ8||{K#?$ZWC|=7X&$eXp zRb9^Fq6CyGSe2?pjo*t9r>t~-wv>QC!Ip8UVf3pSZ00uUj%!**O;G2}LoT=xi(X1r z4A_Elkb;uv%OmAQkVQE?ksAQGshjyr=ZBv$8hck0%dpc}zeZOGKVlYNZ)eI0@bW4c z?SNn0mgtP2-;4bH=c&27J+k$>s#9SN%1qA8V*2S8qd@EG6vcX_X63a9SO2{R$} zksg;7zgI~67f7D&+n%wvx$pmeB>KQ?zBbu->T#pH^U9XsH>Fn`Q?RN``P(wBtM3tt zRI|(PzDDllT5F!5WMa;OrfV)RCnTSrd<>pq97H! z6y`%r$<9ekGya@7RG2E!BEgV#Lo$^h7NRuPzjhnKp`*ZuH%<>)CQ!D#cGF^Y7JGkO z4$1K^i*}>OwYj9T)avDO-a!V*>Pys0E!B4xed0IDIw?9{E-^ZZ=BKTy#A&g>HQ)v* zFPf1;3>XZUq8g~&*h~$_`Hl@i{IJuL6EJsxEc~5Kw_8oxoQ$ot)v*Ud$JFN1FFQqj z0i(n7h0U%4n{Dc;$2NcXQ6+G}-}SMWHt}2KPFZ;}Qb+Jl%yJjxpseI(e zh%vO?1v=jIzUcn%t;pE2reeO={vG$VVWFPZ*^#EZC5(klqydE%xw@C^EdHJ>3(x}>Js=ID0KhLa+S|9k^bx z?j`8D@~8!!(0G5}8ITOqr|pwk;LGvHOVvES)bis%z*NDF_0UQYPz*y}oLiZjSrmOa z;J`|Ny&I4Ui!S300efoqcoQ&_YS>Uo1%3f)0|zBUa_ zOSHlsjrNdIvC5LlagS}Uds%5b9l%~*b(ta@dES2f3x`FE>PN&%*vgPeif!26n~+WY zn_)N8W5U?cAM#AuiM~Z#R8hPgrgGf}2&Fms=P~21qNgEzM)F#12!y3`Myv8>06!%lC`pfFwoRdxqNEN!xum4R6l2rmU& zAkC+QBpjK(oMIYWbRir-S~)~iGII5B8v!*ylFlv-6Rb;8z`xtn`L|~!yoxq?%I`f{~z*Mmn){2 zlY{#|{~&%`C%2WXp}eOYLD_G&=F);xEeOp_WX z3de1%Y>+MbBGToOfO9a#kM^k^lopIm8nrYI^pSXZ_hq;7_K14EA|!`N3O5E4BJWQQ z$uvpGV2KCf94pIBa%i+kmSqvxtf^};Dd-uVRoDpOgZWK|^<=NzfG50hRFdToGZWHV z%{4yxHk7r3q~kAIGws(j^nBAj#{O*t4R8=pdV(}ML6qUpkPwhs>BCeTNex*u0JjA) zMwRDG1x=AQf{w|oR&zq==65zyZSV4p>WpukY?1y8qj%wRN|DLq-XSjc5f6nw;bL3<(Ke>A1Z(OJnK2T=Xhy>D~lli_*;=l>5UcFYiCZPe+M&p~p03jg6I=g(b zvHJTsIiZ-WEcB5o07%w3w)2D0eNAD`#yZLY6Nj^c!MRq5EVhdfO>)9i?5qo7+cosQ zDY4mw!30^KB9&&UbbC2W%6)m?@|LfdTplHuswFM6h4WQ;rMrl<#$!DPdP8_+^v2rY zV%|(!Zi1IKC&+@9W=llCv91W8IA@@ST!v|3_v(~+zDUs+_#}t6%1t@ZqLD^6yTDMf zYK2dejL~2*6JT|Xmrk?dYZFt85{xg2SR3O}{FM*m5M})C@MTkhG-&;LfZ>_e;hg;R z_t$s5eLWKC&K7Rtw>)GG8 z8n)(Wsb`gg__P58kL12I4F`Y9=0_EUzUeF9T1(5>M}gWNCd#Eo&SCil>l%>ad28YV zGiktt7pYk`qgXY=p~mIks8C5&z2{D&DXTnAZYi61o$yJgvSC2q)7`b&&PSfj=WRz% zCsqqnd{KqGE!J($di?C8hxb30PCIKah&2p%C+1K4cc&hZijkEjz3|{|dk*scZjT7k zKkk1YH{CS8hKKx8XntJiQ(qjJa?ep1GyDaEUK*AC*7*(Q^~pzfs_4(lzhOaA)5v0EW`N7S62EKE+&f5S?QQe^74mO3?8he3-CSRMtGFI)5zDQj9LM3MuG>0ak_jA0^s zmE@pqf<;?1|WRJzKKcFH*O}Wt(ZrWnTte5^#?;S?W~w6RvH=r1OWh(x6&dWorKGUPAV0TZcw3V8wN6z zdZSH~(RE}ux9=~_ds4GiFyd>axqeyP(LkHqtc#P_P+Q)*G%P!?(W9_S3fB>(3B@PM z8lrPE@|kr!Q$BlVs}cM{0qJ_#vcubQx#)1s&M3leE;(sTU*c;+OWV|R z+pR1u=7hQbZL#J0L4Xz7=q5SRrs3zS6`Rg@1xhf*%vOp0+uY2t&8(a(YYzsqC{j};Q;IcNfz$j0PAv!p@V~-%4vjkr7vWp{ z;8$jtPCu`7l={Qgnpnb^QNXN~4&3MS9H^0o*J+$^HHDG<^*o}7N-SjU*;#A1; zOA&iA$f?yPVbr-(&6 z{kyLP=6LaJic!9QP4Tkh4Dt}td-zw?je^kMtL`8A?{6A&k(!%4C3U2?5XfTEMNi6F;8xuM8bVswn}hGgP+9dj;ADvjRI+0!znv!mHG{vN%pWW%b2m9rl|Zv!9h#zMKC zTkQF6MKMX}P4*fW{4^6#!Kwnx~78phY{px*au6f+s6QLm&$zYMieqH_DlDx+S6DMby-SO^6 zg%0MFxWEbh$7Whh-GVkVK(wjfx~fc@njrNFgEQ1iU5^xACzL8w6il6H26-PN`i<(F zUC{Xp)eAH#?zVr^|I2bgafFACXH%o(Z+&Qb*QdWwbHUL|Jg%rQ!`AL|qwTLp=ia~n z1*RXYx`XXQxii;;e%n=~$gJO-t?Ub-k%H|b&Gu~vd$|?a=FLn@-)|azf!A6OBe}n~ zoc>1XtxTx0t`UfxkM=Gx3iVogj(DlAuh|bTHPX&4wXZCHM(ORde&HfghBVBg($(>l zY-(I`0+!)zL+Ve&1zoV+H#L=@=1WYoZUUd@?f2vHF1sg(!sd%QWm_X#JF~3VmfwA% zJhoZvzid-SkW4H%nA3lfkcKy)#Bdpo7pscC6dh{~VkvqOE_`fY(UJv*V)+_vsV`Vx zB7Avx7a9=yL$$V%cM7FG`{nyuu4J9b1NZd}W+(3bjji8xBYbtZsiU^@*lMbO&NJn< zsVPIWfL6A>i_e>ywNb2)S7J0f*KkSVRJ6*XK^^o-Yo>d?`*Q1TaRbHCu;Y!%YI~Up4cKSPAV;y}cSO0c8M+#D0Iloi4mz|i68OQ%Q1%%x6{|9%<5i4-hV7E$X=>P z>CNyx5jB}zo`$n0Y&*Hm>v*T|**kFaeCfO(PLAeX^_Nhl zSz8TfS?`(mRH(ViLBD$A-8=Zg=i6@Po7xz2Q%;5_+KV*N8|Bl?OpFf&x zwDIp)`Qaa2pf891WfwegE310TtQf4chNW>y%`Gp_?}QiIXE7w9s= z66ck~l7VGdIu&==Gyb0K6GN+G(^f?9%vYHD`rlXNVWNgS#$z9~((+~w7Bim@O$w2n zW@eMCTM#d;9CpUyQ<@?q3~JxM(&u&>Zg2Xt*5UW67S3HI&IP#VMN2?u!>*ij)`~V& znk)qfmnX{Uvow%5+n^6vw8VM;2*jZ5J6z7a_{L^B$I%PwUs+npW`TqK{+}q%*)2)0 zxhN4FW^51LH5ab!NzSd*2B{Na7@`81RmVsMEpnQLvD)$cRQ=+3G3pI!;uikUpKozB z23;O&xIT1u=cro~s?BeGpvXeF6etFyiOvN};S8|pqNh&Ww$3C@nQhiXF<2&0i(yWf zrIE3aX^aQ88u%TXwn*2 z-ZtfX&I3OEq2?5so!RFpOFqpO8#7f=lI~i)q(~>#xO8JHNS1uR-lQr>4N?@*n zz*Kv2{{YRP2PZLTZ@&(JfGXU@dIW16D!lOxCiLYUhl`1a{uxk6w-4;76uBQy1arTN z+M}l6t}7~TVsH~cFG-oyaNK-xz*py!mN$=$hSVu&R7azn_wdU^erjIX%L7%pCU? zSIjLeW8U=VnXR5eh_(&*tXtPAZ z5b0sqtbBGdHmA$t)pOs}ILFPXc^XV^_o2%s=*Mu*upQ4cGlc8HZi!ZQz)hG?bI$Qy z2>Ijc4{2j<<13hZIr9;_!0c!C$%h-dJWjRB0k=$b5j{U;TVr@|gx)+bN`T`8zhid% z$#9f3rDEUK01%=#jG!8~4a%~tFNN%df|;K2Ox+SAHK{e5Z$h?y2V5WZB)SA{Uw+-f z?1V0~SXiR1sz+1D4OAx_QBsm&UPoy0LQQjF7>-V^Nj(i-TZO0MKcUSc1iaGRWF)4q zJiE*5sYdG_CArJLOL1k_27uDiT3%8-tgJB($biP4PB?$eGrf;q;TAEu9yA{AAc6$`v5?y3AbLUGZ3=_-%$ zricJY=*EDWRqZ_}EkXmroNKN~YAdAYrZsOlM5xb=Qr;M|_49z0ym*4ndw6u301A`@QAj=K!OmOnyn}9wR?tMH;{T@9Ub(A;CJOgi2+B`>b>S*PUP&zEIAb zeclYuSGv*tS01yedM}LwNqnadT_X+IYc0HUGg{1rY(fMN~t|%A)5wm`KY!z~A zqph7wd{htRbc(j}MT!$!M&>F+v}VPN?%n%y`6FO@rgYwYrhFkky1!q(loKtg<&DBp zML1F9Fr{&|Sx0E7dvu6y5n5}WUs*((oz@&& z?>lD)<-|}^(@Oi8qUIJtb>GOKdho6xSYi3A4X^gtZEJle-)Fo>uu1;gx-za2a*&Gg zL|cjK7eUypsWA21Z*U)`Xbyidvty=d#BxfGKUse8;r=$4Q|ZB=0`0}|-E32SPrX|& zbs0{E)1wkTo8AZ>zWymICnwLU4R((dWcYRKPr|~JBUQMbNjdzP8#Sdkf@j9+wygA! z2)}%e=jd)gu42648f)uhWtAXxGGw5Ci|3R|SuXlHVJTI5jDfxo6zlFrrU#-{ic0JN z<1w(+mM(w^s>x~~?s^0*D8m)f_svsi zE6LrfTOSy_j=OZWwfqscJA_)321WKs09KK<#?I+~hHzJ=&7wNX2j}8ZS}4ASec+Xf zjUIBdd8MTwU7DD*>foZ%!gpZYs(wV+a^br*h7;4GjzdflwUBp*-rr`NT$i-=#Z})q zD$h3Acw-0xkB8qkJ-i+bs*U2TL}J%q<@kduTOC45~>TxdC#o!CQIS7*MP#E`(aC9Z;Qj08g50kF zUCK}PCB;Rqf3;N`n4wg}+Zm&WyBIuOHfj0tQyM&S)(X{6bQ<`zgqW_1PwOIVPOBQD zYWW4kW~kYvY1=_WgMeEG{o7bddJ%Re5EcI$$Y)jQtT(S)*4Jj&YxxAp)KfgX@Mr|$ zy45TKvPpmoo=f3G>3{J4_*ZVYxU3Y z%#U*M`lVMRF&YKVQ?@l2UU?z@)~#sP6JljZ22;U%2odlp!QBmz0AuYeWegCd(hLes zFdm52;Vpo*kO2S#5q%s!J+RCqp&XmBXR<}E65pS&)iMKX2<#+~)k~veciC+6vJIw1 zXW88ANfcIu)TxK@H~b5{Xe)N-pFMq`yi%ex)ajC0Z!lL~T}V2PP0b{K*Jd7Wvy^uN zlmf||t$ckJ^Tt_}gOp-GY9?Mn0`Ptm&q0jUAdijc6XpuNwvJw2%OPG@CIuykMu{TU zH3O?go6EQt4NYN~DXz&=y-p(&)55vjLcwgK87vhb@Ke+%P%oEmeUZaUl#@wm0=+a1 z{~6SKZMrnG-?oopre#*2!uenlo5M0e$f}9;vJ95uL`8vWh-kl+1t>A8Vy!!%NvNNUVv|GSdx!8)AB_%;h@vv!MaLXtCHKb8&bDM^7jxrTzkcI?WxWd+64y+8XETzyjN;w&fsI^x&>{zx~2Il zh0nQ?9TPvZ5o9k%48}BRMMZfQ!rrC22Hm`?N(BQT!g#ss?x5RXL}PyuM}A_@R<6$@ z&9RAv&n7$MPV4NPUY9Z?5{}iS;0nCj8s5j&o zb`|>N0^T7JhaAmpi%$|{G;e|pPnA#TOiYoCF8a>Pw1t5Ofrqf#AxH)%Vatp>OF@|u zsNz*8-b>j!-Y9k&l{iXr*3`^54ucM&?NqoLU!=eOn8?5^f5+i&U%*3979}Vr2XS_= zK6bXIr;BEI!^7lQ$Kcc^8zy9n;c|3ge6#1f5+&L@Tds0k!xj#2k}|fKYtLEG^K0o8 zSdz!A)rBrXnVRqot79_jX(gKkZM1dh2Ms_fTwd|H=6cmMKW@$q#gj~GpzrvEpfK5C zT5{FUF5FLp-{4FnBog@e4mdQlNqHjxEiMi4KZa$>^kQ1scW=BzI0zD`aYU3f1@S7G z18mhkedc#UYc=U&MG2L7YIoPN63fz5R;+QzLzh~qblyRolmO3R(9u%`N5pNOQbg9_ zct|t_M2$6Vlt#@%l%jA5Umf=C^4{<*9;qpr>!}*m=rnWkPLRR)HIFP&Mo|K-Aa%yr z25$Va_Sd05PRCTtjB!8heKXsTj(MU7RlOiC%Qv0gOw{v-*Ev_s@7Q>G6qcBy)`U$_ z@@=LoDx@^2I>ua|JH%uhz)S$E=*rVwC#ZMm^{dqszqIM@B`2Ez9_%fS4nDk1oC$C? zJH%HeNdlmujlueTJn=OFP)!B^B8d9b&h`w~5K(OmzP2HwPc-t^gUC>wb9YQmsb?r7 zpU`bxuOPGs`!_8+j}g=)bBe1n3O~d|QVidyB-VV2ng@X@AP@!!HJ0%#<>lkjuY^zL zkQs1SVBK^8eRL8@96osys$-K5VyIUwWnp4S#gWMw=A-VJ@z{j7gH?C(CGTw<(E*53 zY{IPd;!AQKCm~*}FK%kL`ls1MhE&;?C2Al;4~Fuu`CmPXXR|@oHBFV6rxI4M+McO1 z=h_IJqW9V&KxX}nG2fG?u8Oqhw9t??CtI?ft1-#13g%>daVCeVa4DlslVf?Td^d;% zIRfkhVCgPpDPl^ZJt*jV@H_RbA5YoMTNijV`4u!*6N_hUX-CI$wMwkxb>|4}rFUf? zj?L}B{dPAHE5n>druGxAmfyF?^X3Fe~!5?rkB3yU13+~tNfynKQ@y=bIKMaWh> zxl%Vv9tjB!;9yLMT3-I4K;LR2kXVMuSEFPDU5i;{8zbo6ig9Zu#F&98o>xilzvAs; zxNuq*(Ih919#%c{CO9N07d0bso2jxbuj--=NEPHLUk$YCG|5__z3HxAAcu45Y<;Mt zR0@?fjLD#6V{K{#XmWpns-`P z-(Qn2agDX^8h7a#S{DjP38Y<|kEtH79;+~(`^i_1r%dt2;ZsK{KEPG#L@^H#;-pRt zfS~xwX_H}Tek(OHA{655VSJN5)L%+(S`pCr3!hBz4iixVJN02Jx#D#LH zCVtT1FmapytdLG6!ibkpnY8!A*|pg<{nf-I74D~D)jgMxo1sF>gE_)Cvu4XuB;zAx zl|sq3P45tUuldkbp6%4d<=XK*uMHDGG0bX{6nqWX8%WunvI{~UZ#iKL=X0FLe<}Dd z+{`=eL3bi<0)I+!lp@1-sC4f-bxg?w39KJ^A|wD!U=RWj(Bk<_)gFr@&(gBupdu&2 zOvrnI)N7w_VGqCmWMu|eY{1Wt+r8TYhV6w?QhocZ;mnZ)&&m$Dv;MA^hSkoj_y}6n zGxnwPR1~=A&#Zr(xEP`+r%|TF)@MX)Bs+~c0gypec6uD{7Rf2YX+T?8HwpT-MWp;v7Z$Go)dvE7#6ep=%C_^#^tMkg@#{zWR1A;Y{^q%yYnfXT zexWiXRYdzPSX~09X#17rQtEOvVS$RBXb3Fg2|GkQ5jgO2=Kk?Rb?*%u3)WI3|FDwF z;MallsLXXaXbR?lu=`CICHo)Iq!eV~kgy#r&bf2`(&K49{ggpGU2Jy$y^pr%knwp> zgfy*fOxhS<#PM{EvmSB15p-gkqdL{_{dlG=5rK~m%T#rCRNi9}x1;>D@eTEbo^`FQ z085`{?gL9I_=b}>k|dwOEFe&pv{*>Zj5u>}2`?3wJ~{;!o?1AXG|W3RbvoL}Pm=IT zFbvsanPye*Rkvz*E&cg3e5u6M>}@Leds_JAVM*y#S%*$}x_l2yc<9&HyB}S7Dn4|6 z;54?fgt_1UDMV#UCglfoSffkfCp}agdZVSqPs8A2U1XIaVf~REv)=a;A<~FUJ{ME= zPT2-D<6j)e+kWZ0p@&2^*{a0yF7%Sm>}?Y~!0lUrzr7Z%vA?(8bcQycV?_}R)~w{+ zB2NLo9C|{3t4x!9+Oj#{)6~$N9}lNx4(N%)Gij6{ku0OwuhCpy&P>Sh9CHrXD$%cw z(6=LoSz8w(dK$&dpVER*7~|tPe)pCvE$(k+h=oqaK9M875 zocG9mQHiGbHkKXNCbq5ZXUI&=zoM;WOir!2J-Y0}@1r0^=fQXJ(YS8LcPS}{%NM+w zp*culKHk7@R-5l4z!kxCjL8$g_&)ADMK>3Rt$X#!StTFpel@vy#L}}U$YvE1W>Rp+ zikKRa4D}BgQ(y>0T%EweHvi7sijWJp%YCu(>GGdkM0S(>KM~SBJ_~cu4q;v_&x8!} zGHUcI?83Q1f5BtCIgNZhA$0Y^IsY^jCDS3BbOauRU zLoZ*sdI{-bT94frD#<8H%Q%b-7Kq$bktQ3d12>qkZ=QHPW2x=e4|Vh>J^3ALbDLl3 zY+ZypTt8RE_m3^eGQAG|5(M|UmJQN&?kY+3_uHxJTPZ0Hc6uh|^Vi7c3n}M}Fa`+? z#WcRji$D)V`P3RqlEj6p;M3$KFrl9Gn0CrWf5GC6-cLmD?2^^=>G7^=6T=7$F9in& zoEV+^+(>NPV)7^2NB7Jkv|Ya!gHD#HDn=57ZOuXPhIq%=X77wmm!}U8_*#EIZ!HMY z&Zb0EU0^m&XDyFv>MU01f~C-iv^j0~YuP~W+w;?AH`V-Vd$HK8L5lD3d z+S{*K*?q_F!>6^(MBvY`FqLJ`UmY3zTHwU8--wD;FTq6GUMMngs=?tFp(lH$@%A6YoGI6mvx_FPAlm48wo!W&5QSA_NwoDqq)eI( zMQ&TWiiOPtkk%6)ev9KfanYIiwzU2@+w7^@T&vc@|3lY%z(uhvi^GeEAc%m1ey1tH_po9XoXs%=grG(Pv6(?p3y$v>%H`??C1Y+Eb$4DEUTsM+&>5KY0zbyPW+K;6^M{lniiZJ~dC z&%j%i%QhoMOSDW)#GDg{6mB88JXB)8zR1N$1nz?Gn;{JR|e#2xvX`(DQ zv9<)0ZSWwoiYbC2-KjV}8KtWm!sTn!`8loRg&dlPe<5t+m;z(*aL0w`xo!#=fxm~ApDdTA9$cYV! z)zsNlXaTn}d4$1%e>dhZF2rBuJ zXxNjDzGb~3#_n$CMzKGiojx&5psQee9@I$+x^ZiPjW00Fbd_*iuEf``DFbHNc|X1s zUWGh;@-~gMFh#LgwH8;7-drZCpPev8g;Z5?RW7Y4V)(3@*qQbIHmF>Zu*;{^B zTF{UCZc_;ksC&f`*-a4fyY@xsw0_;zkJ#B>b=m2k=(KeDY3Sx^Ptv6{38J}Cl&B}# zC|Q4RUS02j*=HJ0@13IPMsJI#Ht2+#>2a)9{%XZ<5?e6PruE=~d(T)TW?|nmDdVvRrA6I%c`LV!3jweoNReIJMk-SjYuQr2ub;Knr@Bu;9pgPA z!-UBdjG^jU@UhIa{!Z@ig0|ve5DJc+t=3Cz65YCW;``-}#t-aRsg=Rqx70j8bD#9U z;XP^XYAohl?SC5FMZ> zlK6%~*KuF%%4b?|T0x}h{7sj4g`l?BdGOqijHvyQv~{q^!SC}*=Q$I_t`pe4lN5r` z`^5EZZ?4yvvo|tIytHniGyAq*ewt+TLU>`69o)1&qZTpni%-whDQGKJIYEj%oX7jO zI5l)>Aizx6MLT_b*p}vDZbFNszu~!IzWyW~L3*3&@Ge=GywvyI+S%;{?toN?Bp7R; zVw+i5@GR0#cqcpixt6vBgyHv|Qw;PN^VIt1w^x-KeD_Vea9*Jz8h0xvS>A~eA8(6; zXK%2H9@hjl1b^I9F(mYX-kYuPHnGB_M5%&~wbzgvrROrtx z#8rKFTlOVH9c}|_%qb9+$@qTfKyBGaG<};Rtv_KAx*N9L@=0H{<@06J`+ID@X$=l< z-Y?T~iH`z@-F1m_b*wJZV4g4q;&@B6UHJwxvG2>PZik8%+aXRA6nyrko^;f|92x6B zeOs%K)*PvD^qzm|)FA{PN2qo&m#U+RgA?@ft@5fwhiX;9;_2?m zLTu3+o6{{KHZ#t0Px!x~-%poE*fJ*uFA!Qw~4AL<~A$p9`r{T zR-36R5B!oEKi*1)`P_6-mVD$!Tr;&mYq-cYntwkc&lx3=oQ##M@kC=i#&-^jRtoHp z*x5PirTQQYz8V9hLXdB|Uw#L@GlHl$QEVeDG=yV?RSB&*k>txTpSv^2Hi}|=% z*-<(XTlMN!>Q?o1St%MFLYx#fM1T4H0f}wLchBw({AwKLuTQD~Be5n@mq`gMopS?p z-9C;6D#(x#<%ItEom^ym25o9VroiNwfom)?yR~+&QD4nhmQfbMdJ#;dAEu3hnIV1; z97`Pb&RUeFr%|So!tqJr;xczZb@~?}aBl+4P@&6j^h?$BGql4fW_{vOf>od9z|Io{ z=^^Hvo8E72nYJQRQAB41-ISsVuklE!=T1|pMVIBb_H>z)xE$oCeQt4OBNd`}&Jnq- zpFpKsQZMfFwoJ3pd%Lmb9?MDz87o*5{_;bl=ERX18fP=6nXaC;2l7E@%T-{|Ya7V{ z&39}-IdAP_2Dx>kdddyvIK^%bgS`j#EJ}=MoX0waLhOod`8M(odSj?nkiR>VhPZV)Z_K4Bx}@YBu}Mez%tWXxHHr zDgSd4!riWTvjRTj^u*}WmNS|iZiMPnyjbrmdV0e_!E}_+m}(TE*G^G-Sd`%Nf3Dt;U1H1`PpQJl$tdzS z?v@XO7tUR!U#xtF$C&$5T}!2%+vsRD zPt@kg`8i#N|EIfuK;kUwc1RzLo9KuWN-APG^u{X8Xu)p>(J@&jVa6Thwd@qrEai!8 zYS`HP8D2G-VyaT2NdIxK2Z3y9aBF4=@5qY0Z8$)6vJP=FI5oUV?mhmxM+Vmu^oYn9 zL3GTI!Iw)$AkYt0SsbnuDtZO$*~Se1QGFTpMhqFjP|04$B?@BZTpL-kFpA1~-8WzQ zt?yF7xvGW-x|KjL*fdF5>Y`+BJ;ccYBM20P z`1eJ{ynN;62St4#G0*}+&o%d5I83~D;{>cDyHOu5<}g{9kIL!|Fx!{|!9F9trxjH; zSWLPb4EgA5S1-&oE)-6=I>09i%=ccb*W{2}N5vABJRSeebLXx%_}N>mxLBD$rE{jd zcN(*a#FSun9h5WrDkOJJWoyaTd#hS+#0>0Z_Q{GzQL1WdZlpR`k;h7AqA=2^tU?f2 z-(!f;tFIjDbw;4$SH&1AcVnb#)%-kMV#_nEybk(Ywo)bcTz;*j>o*^% z*WdU$-!ni?YOP(OXi?Qug&MtRdXZ0sF@z-SRAd}rCWOv@=Qu=XH`xKg>3niORys|^ zf?mzGC>(Q9BZxwG*~wuF{HWcXwtBm$e>F!hr!_Wk+$OHne}RngWJWD zIcn5vshJ4<2V~W8It&$fl_=-#f^M#oe7u}!flzXfU}({@%S`Gu<+K?TvK*CdpQpjI z)QOA$I}k4OsOFF4l}x~VswtibtY_4-(Zy06yg0WVQM?($!?k_Zf5)y?u4vN7!$({# zK$_To3g+K!@7rW_v82{;p5{bpk1gQs=X?^u!&RHrNR}U_*;u9)dHnsm?U(tPGKCaS z3a-KgpXiS&dv#YopA4ACi?uZd%SKlks#&YJG#!JN{fJTb@;-e%I#s$;1b5%h?NdXs zv%HFj6{_24KFn$_9r9p(*TVQoc65^2;Pe!FZ}KyT?agt6+l%0@H@=(Kym%BpCs)g? zivRg$;M_O)`zg04=tD(Pd^u~8T85_iL_vNmIgUV3ZeCKk!7quSsbSBo#B>hLdBwyC z?ZYOobuOzf)s--ViX2ylVW$WQ*i)f73u>uPtyT_a%WUAJtVyTs<1=iX56m$VRg1)y z8+i9tte%sY#Eg%3N5fn^3zD}Vdv8GKvgbC8M5}YF^BlZ+zMi8vZt-eFH-nm~jR#ZJ zx!xLGY%G$F73u}Jf>k>wjTM_aP-E$6_Z_F4;j0m1Jft{MZ1{l&{fAa@Nfrb&h*+XP zoyRs`DpYH1{kXn!4!svLHZoDoOjJ*vxmA?c6NW>zu~sy*IYfj+{Jk<$r_t!tXwkJlZ=Vk(OQE6gM{YW;f4D+xfve2eMs}k?Y=kxP{!h zH~m;FG8T=qh1Tl!rsxdaOfzb=6e)RODgS$(lM8;Jz}-d_2_p> zOxYAS`pU=SRV);|n-IhI!cwzx3A$`I%l0y^VlDFrD51XKodTs=ikDjquHviU>@`#p zwJPFM2kc6+gg8hSYBdozb{RW@co(P(o%)&R?jD{i zzvi0_p+#C=8o?ALFHW3Fk`%~y?lp-Y+CIg;>1nWF%S76gJ2*u5$L}G#T`a>fE>NGc zld)yDv#*VY5S^$*fe#;xRKe7SWs2b#CXk>e&eEHZR8)%K(I7UWMgDvL!ScLQt^mqH&jqxBCYEuT&D4&1>x?Ql0q z{#x9s7~1T?P?A_#_UZvM_f7I|3mw)D_rFEfOwJ|mscl&1y5vp}cIZ&fIusemJ{wR$ zCT5stIH&!XqN)6_+$b`bT~talx)Q}^U_0duA5|_W{YC%Nvg+)(BMmzCX|`TA$qCAV z7T48x7>*U+T5&~Iepc4To|B>x0y0_d-VmzAnXIAspEFj>z(eA{LEb5ZL#F>+k=y- zF1wKF#Yda2?-dlw)cH@Mg3~HgN0ez?&wg85(UR?HZ(zhSVnpH*BfH=xFf95FNAaUBZ>s>rnLkVT05KC{~SWB8O`l<2Gq=itcQ@1&r7st!5vhs71r_pQ`5R(GdE>tEjN z(X)%q)BnM*t0clYu?MzewJ1&{e(frBU%QgynU_ti7wfxPQkkJs*P^pkm>V3g82m%G zUSxPb+4@x&Psd9giC;S>kt*)x)%%>1*l8l6AowT0b$oRx^n7-2+X!n5O1{$etB(>y z;N{Q50l$;Y7!yKCn4;4iKgodDoh`GrbRe+8Vs`ahS=36~tm5l%s}PI9*z6=OYxeaddUB-Ag@ssP?zL!WM_>j70xL;Q+Z%?UN+m*_HX0hdNQ>I=#iY zZc~D71#F6KDIboEjx*kU8!b}Yip@(0|7JDn5zd(jzPlgee^<*yx@>*3V?O0o;jx?D zx{n(7E0%EE5OG0$TZczWv!k7II0U$Wp5B!ZO>;Z=9j9`XWO-zhTKP3weqcE^ux*A+ zG)_4Bm^8P)u)Z*ddT;O}Z)CQ@EVA+YH)#ES{70!^1 z`Vq<|;uqy{Hkq$9b~bf!X0Rd1mcuI9ldd%-IiiR+7pBzr21w}^LBHNU9qrJ7Onr>5 zK&)80mKRqS1tXY^hK#lm|kTepDrRer5w~laKkk4 zF^K)sPkoDrqYrqE71hW?+wVT9P+Z;H7APxLY6qex&1(CWyghN%)W%(^<%@|G;DUNY zPXphaNOJ1gs63(7ur9eN`!YG|sQ=Ks@-eaLQe!R5h!~!tDUcYEb!(3{^RC};qVbAX zm5ZO@nV4$X85ZBS;n!h!%P@Q}tqWt}d$(lR>$``Z;N@eEQ$8Ol9|wu0PRaVjC5_|S zY2&xlE~3AI|JS=EqZ=$>x&3qb0(Wi_cG{NB34U8Tk{h_iI;R3skSh zXrAH2VAdJoDyTd+*7CB7pV+2*5AOTz%mvE*?F5C~_W@xjrSwl1i36VR2c8}@4S&^7=U8=_v zK)_D~X%-&khb0rMiU`i-PC2NkKI?zhUM}FAdZXTzH_u^3Tt<&zjL5xzz-bVdDgMQ6 z#l@Wm!h>oPKiZ#=5qi5Ild>lh<3)(I<>U^It>w2&C8krvdO-~)&>1Sl z=&5lm&9AMMy_Gf-@ql5B@ENwDO&RWKlDc(C=g#&<|I|!gB*j=y>H5I_~MVM+SipSYlRBM31vjRA5m*b z5j1&lGiiRy(Fr`&kKx@@`x0WR8|d|#ks|VVAvDL$ZD%|7*(wz7v_96z{4MU*zK^F+ z^2hHWQeMET>i>Y>ELNBzQ7|+w+WU>e78*9(vgAowb+!#_YB*@tytPozH=P-rUK9~m zpe#~6ITcGRFUIqxQb`G*h=cI49PohVoS&nX}YEor0x$pRyK62LLauVLsE2_{UQoY}G z*f5nqS;Wa|JF3Fs$afp?L;ED4%vl-N7FrIPr6aQ=G*4$>s&xcF1_kP0Q_QHdUv z%Ptx7m}QyZG>MKxLW+Ll<2l<-a#E^^z?}D`+S7EIrJ6v#DqRlpTkN1)&%p7nk47xR z(n#-Wd}EPk8U#5JdsN&q)sa8EF~fwT!A#7AL;aD;Y#2{2a%#}z?fO>JH)8X*uW9=5 zG!ty?CMxr5CSsFP#hiI_}djJ$&%o*na57Yl4q>s<@eX2#gtkFioJ}S5^i$ z@iM0g6iQKY-?bjC3nWSVk=$+2Fcm7K93fyGA_KZZ3c|hlcho?@Z=mvTfKQASVaHXY z`7DFa^e#*G^^HIR!VbPH5GZienjMq2h8uUC^~M@-<_(q`8wwSIvn4B-eiEX*(T-!y zq<&K;s3V{D4yps!p5@uc_CsqeA{}a|)q- zojhB?grvFTubLJmH`1ucYDPboermYoD)*g?AP}dP5^r-(QSp9O)JwSjMMB<)>(OMn zPb*w&V`wG_@Rj;Ma3gV$Hs7<6qsRXGl7z|j`&GzmtksH`1>i;bAHZ2R$#D++pfIjb zS!hf8&e7)eb;>Ld;AnRnCvemhXW`XPvKtOGw=<^%sf2OmMr)w&o_g38q)k7aU>Z$h zC3u+52Qu&mJ|aK>UbfMAAdn0Q4+Qv^-m1(T-7j1vAJ-;c1(H4q0_F$iJiNOQX#>jW z@!~#BYX(9}g$^q`jW2>9So9oL$B4;YfBxM1&@RaSF-sIkM)A$1Xn=sNvc2#+r?`o4qjaHe9ABLx5oXgc%!v~|JU)l z9LC$|E;upOLLC z+biX7k7BUEiqOkW$-BwiA(*8iFcpf%15z%^!g1PcufAq~QtA&5bdMb`Mj4uO%G4n= zsb+xwOja844mCTgs(^~jOC(Wi=Y4FM&19*eP4syy{EHl~K|3>@@3T5e8N&8a>pg)~ z3XS&L3P(6hk4WjH2Jgnmb!uC@kA7(ieQX7W-n0LFHb`~f^yRn4${Qe%Y2e?Q_Rk{j zk1Jq};?!)sMHRf%58#evTx}Bj1LAKV+)DFLXzauAmV3fP)eXcP36GWwdKY2J0U1}l z(6(d26O;7$^|n#48=sh(*hcfKeoRs)<_K2jPk$ncEeI~CynyX6{r7D+D3jb{P^pH? zD?Vx(mk%F4|J(pFEsv6q!`)r-p6GmyY*?R=XTeez>p)q`?$@$J4w=7G;PtI?S9QIC zV9X9p*B;LS-+v_AbMNMgeEbhN%y>{BK|Bkg>M%{qhpQtLuLe> zef$m-@$=vY-=?+t^LB6Zu>=On{I@m#E!_~nuhOAiaQjaU?@c@zMsEKgKuX&R__*D> z4SkGNY5oJEIhSTrXTci!rEXTm-c=lBq-~WX!8xbnHg{Wa-!tK?hX**1AP%R;7%33P z^_z6%Bt?Mp^ESG|iH8{50zs_R?_Wfm>0!8y2Z!1+{RJ}r^G1LT4*vlutqmnn{d#tu zBt#^tvQx^bmbL38s??2llaSxL&esB*yDG@*aP$82Y@G?4q!iWRlH%d|eTj9rY$Obt z8P4ZAw4@HRO48OgXNE;QG!n;2ne5qR{T9euJ|P4 z^}$Y(wyv!z3^C#vH>j?ebI0FW^!MUT57?_qqNFOc)AH3{&Blp+_0@nV{KRs@>Sc#9 z--+mvRIa>L0Mex9qF!;>#CdS&mwK&V`QoqGyrFO5O%(}$Kr-tX5@vO~Gl;+Cc{#*U zZx71VHq_<`U-A|?fYOgLZ?v_n56xhq=>3W`*m5d#2U`3GB(*)b352B`h~#>u_y+VJ zpxKs=JFF&5a3(;3|83xLz<9&4jy^HdGWnh!Wz=(wnQZkB@B}cwU*9qK1oKi{y-IT1K0zGs;0#wKg0SdOkI=$ZMl#jS_xnzkaUo z9Vc5BH{IO{^dU)m+OzG`yS?I#!G8R*|9cYqZ&UtLx^w1x(CGB;D(<&`8n9{vc_4wZ zpNC^n@V!AFG9;sV%&0W|*!bb}KwIW9c4T-bZ^W4ML==L1OnYRU>mAbwMMx7N62@Cd zb{coAmD!9orb{kP=N3lR4_Y@)0-)0~{$}<|$(3A(ennVH9*Yf=QAX-f{Ev`OX(;yS zL=kmqEQ@LIu4rV`@FP*Hkn}6SGO}r+rbqvPC=frPW+OkaV<*z6MP0N++!p`zhJVb8 z?Z4ZOvtg3wJpO#%M}*p(;#G;>uF_!0ZLCMAbG7sbzPs2t2A|t(-J9p<`8zV4i5sNX9-+Rp!%qmyO zE3IBgi0De|3m2*A-x)Efuz4u#m}l|oc{VM7la+e<#pEx;0Vo2BKhE6mpTzhOD*=oA zoe}NSJ6Q|N0U)so-Ztwm)aVfPVxdJ=U-}faB5?C3L9%z4JLfCbZM- zy4`<)x6_tH_AM}w^X6M769}ZsE=rMiXQE|8B|A`-9T;`cy!tMQh> zW0sBS$yo`0l^2PfR7}X3)u`3h(%lTs6R=wg&sU5kD+K3g{&Yz#5O}5+&y4HgQq86Q z8A?6n@|M9x?aY}TI%&aGdlVb`Q>=w#*Za&E(8H0QXZ8EwtIxk-(bdCk53wo|sQHo7 zm2&(^sO4EybH$@G*zfI@J;Cq(ikOBo#xaY1=_mDxm$y`bP)L%JVacJE=i8Oo=c_&v z04GICvSX{!`$cF!#%gHLr9Z>+akCv-I>Ud2Si(@HAp6uQd4595*YGL?`3I!_A!qD@ z%f>M`Y3A%d62^6ZbrB?|yNe%oEBI43jtoAp=~uj)EgQzKzT*(ale64o9$nc$J%Jls z9BASD5-x7|e4EU0u`m20^Zz8|-z(`t3mC;A71CqU6l2q8NrPCjc3*I&k7scCG?%y4 z==xfKV${$84fL6x<%#{-AJFdm@xEP&o&6Sw1j)|fsY&W|)3MuG=ze3X3CjJHBzXpS zI0+RtO`>^eWScQ)t1hU58%gLCG@9+;{Gt*{nd5*0eO6QX~GSR=sd3`pQI7HiU;8)w1&9mNp z@S#us>0_?w5qO2~@BPnY>jCmf0-I_{;+PmBwV076+I;I8aM3sjM4Si60TRuAwE#1~ z0B(o>APa7fv=TCk_ZEi_+##aJ)jzss=LIxk? z-m;9?WQ^L}l^6sFZiX2d`~!l(fx%fzuNMnM+UolIPNHB-{tr(?FBK0Zg|k-O)l+;M zy?bjJVpeUom&GfF7$NZ8KOmlp!i%e!MJ+V`va2uKki#}qx?->mGT7E{B70#X1M*fJ zL#{2$Lnqpxm_vUUAP{<8;vX$CKz7%=ePU1W+c9VO2#XD={Q!jvbwxdt$(KuRDP@d4 zFzyTe@$(|j4)(myq|SCFwzs`(^Fu2y$`3ai8Py)<_{Q!Z8T9?De_qoC2k6+!!H5%+ zv>BQI5D6DVdV^m~r-pOC(W!PLi7*M@EMX+L9&R8ieBA6U1P9W_|L5L+6b@s)qsBVU zMFt)Js^%YwZNT2V9l9Q zwq6v5IoP;FAb+6`tayZenN450n(+Rrnq8}KIpez00pzYpn6HWHS$gp{Obhrm(LxM~ z+Yml$-uKS28gC;SXSyps5d-kcyXSqSjtRl@(yAAfdf;US{FC3xJYy0O8@{M1DLZ3S zFG3*C*%;>Q=Tvr#!C?>Yuf7TTw?_95Ism#J*_XMB?26HnZQV_s8XX@BBOQlH2)RL6 zyQJ$+v77C{?Nm(v)hhm5`s+EkaQ|SgXuVl)1I6d^JwCCXY*XG-Ot)U0X<-gA0g+-PREn7wN;Z+I4Lx0cd`yr6oUh1js{2%Y{Fd)T@pUP$spgEw2*jUdjYS z^3X`E1@o}XuLMgeqSbyb&3#e7DS#OvQP(|dg;|{sJC*?6YiE*Ip68C1LOYD&Z6hXD zBbb;@H?2v(lhbby`XhR>F?7Wo`jYZUl0oEzN?x(tZ-374K#{GY`^7kM9}e|#!KPtG zFw$lLkPN10-yQ@!Y|-|znpyz{^@$hTjH{)i$Cb|ERSidg#s$9mkhJgM)O(Z0i%&?2 zfUTpYHUE$myAjVp_RR~d_I2m`KgdvaSJWk zI!9HuZWyWza1IC3IMY1 zrSWr&*O64I&+@R{&j6FqNk-_2tnC%IwY!k9)4oOAXe)jXqF|*W4dD9f_oFZqg0N43 zgwr~4AfGex07~g3AbSO{wFN54 z)9M>@{Lp7me*0^yA0UDU{?lZCEs~qDm)IsE6!Y{pKl_+LCs7AaCh67XHXIwvR(139 z+4|RwgHXRrl^3^r#vSC{UXW!MCfre!6dJG*1SA6haAa=c-~uN?0)h_0ZW$2HNeoKT z^!m!bKwuDZ(bBm*xD^nxb(XY$RwjGUM&A|)iDpRuxnn1?0@V*44Pa~|+EFCi>K!*B z8#_K&Mgz0Yo5?XRk{OE=%65!j?=QcldUOibB&1>>Aj{?4Y0zPd;Xx)2+G-I97tt%a zxPl5pdZouqC|eG%u6uemj5DVUZI1)xx&(T&5z3_5FZ$~3!TRbSkY)^^h9i(r%^03V zDdcggm*Nz1%lPpxf8jPk$g~LT;?wzpJ6dni)wjOyVB%2k^QEy=9(wL1!{KPD&TCOQ z_h&hDxRSvH1C~-(JrueQy=3|~uKzvb#LiXJAZB$ob((-QA#J6dlM=t=MmP0^UyUiG z^?SrOvv|*!>y*)CRF;|lzE#$!yIY+&+%zl|v83qcAH!J_-j_R>tI+rL8x>TNa>!1| z?aN=!Cgr_8k%Gv+gUA^|KvI<#sO!9Z|Av)TzYFG_O6+~4$T3fDkG79qa6#~;hxGFc zD+Ln6-xU*PC!VNQV^z%Sv*~(<^P|h_F)Z8$4iFpbJ`d=3IQ|1#a`nZB?`$zJv^}{B z8Qfdus?Ygt5QqBuPWr-r2lD5y&zD0U#$8nEFJl~zzdFP-k`VqeRBt?J2uKmwgo&CppX z6W`x|A@^@wfG!nAfzhtjza&%i&THu%{a&a4=II`MIDt*qH)SCDi;<-*XTg@YH1DKI z5S|15&1nTFyHrQ%_roThiCvpJ^iO>5Y9_|2xPd+Pt^&q#rlXvc6CdO}d!)N?_g1KM z!ZAjB9C$kbGNa_c@upG#x!%Bu<$);u7_@3|={jwFC)xOoB;AlV;T;PyL@~5-24DJe_7>1M7|UX^zU4+ zCSbb8=c)9YT8diPI)(z%{_=6Ek924u$Z!6a;k-n}B+Sz(7`EgWLap*)<2VX{YutbMYmf9t5cO;J^%kP5%PlWg_X%R1$%Ea zxehE^Iab$edjfQOL$*l_4rjYPY*FMDuXg)(z1BpGu9NJm;u3B!|6??=wPbXT_PNGb zEm`=sxczdPZSzoS0=f+CqD}eMwQ3S`)a{&MWXn>sN%OtumYa#qsz>J_%%rm0PXC4d z5Eb8tcixqLaRP3E{|eeBAOt8vY~_ixD~r;YGpD_`xYovkrK5iFze#q>uM&&T?LWA= z{CSzA%*$yKxqL=>Ak8?o&M2P*D9I)|{*cMlWvx)=&ED}pAficRn^q{!jzSN~?A05G zD;o5OhuAWA?U`-pv(~WRrhoewe=p)PkLip72wSjjmlugW>e&%@7w&P4$lBET*weTO z_S=dt(9I{NDgs4%^`t#ncmpK=r}(m!U~g4Dsm6K7?4i7kyK75w!-;m2z~+-XnW@iM zWau}n*erLGbH?PVpOw~?PHetHdy3hk$V9CtY7>*Gv{JTypQ5anzRH>Nom_s##QlKZbwLd5g)k|BE8m zN1Py6kuo@7#3&ooIWczMdsnFOzgQN?Qd)aBioq68zW-tU?Eu}mX1;*PN;L_gkQ`{v zcT=-Or%qDQ${R=iToyZDTYNmp(R;hp*7|;}qoAR0Rra3hTtd~9^?HJ>YEO-j{#4_5 zQBG}MwPx<5???j7k*`O4i+r9?Mz)64c5qORhD-32&2I=(cfXcMP!l9svI4T~SKzK$ zNGH9_wtPX#S9oJwoL!XbZZVB2zwt9p0sZ_fzM4_AxpjCqTU93+wU$G0{-7dC6|NuD z-*t!FKHmGjVg4t_s>o}^uP4EF;7CP`6DUa-NG?_N&zbQ0j(@-{0;(X2Vjqsw1i(*~ zT_h0`YhB_7H;Na}8;#Bn+hcw>q$fJSoy^Xed~=1EJ+1$C!f>vG-rb9rlN~OMTDN3U zBn4~d`W5x6MrSHX~;x zMu}J#-%)Uza`XYR>g04|3h=+~sItQoKi0Ic)w6XPhcUR5Jj=5&!mlJV z1~Ni4*X&x^-8^Ixr0K!^5$;89J`Q9b5$2^~xk&@M@q+g`PS|yp1AzDAK=rSjr~ZNG z-+n|^%!uO9H?kkKj?S$}__k~a)ZF!?e zKfWcEBYVUYNktZnlVdFi?B@Y2t!Dw-aI=vDn{;gR0AT0szc%S6yLX?I zoPvp0M&kh`uq8JEa2e+Yi0f!qTRkU_0zp-b6StfCY32J?%I{T+=L)T_ZPI$od-WQt zE~z~%mf{?+(RCK+W0_xUsOUn3&bI>`%Qr%TEGawZ2BjE%V}d7h!-XgfR{=)LrL?2| zj7-Rzw7T^N2pa1e#W;NSA_XF$o&M8e7g6E7xzPAtIBNSrl0<}MsDSu%Bb_T^?SZ%4 zgN=oZ4tMu=NP9&~YTk+kaJ~+VyLi7QPow0~Vtzn-8nm z4|~oPqObOpoqQlReE4gWLP`yug@s%(r%iFyqt(v}xukV?_tG_and<44UM?e{R;)@r zvM?^ZN473c3iH{o==jY7yXue4R+3xfWPdUra+ynIC*QZx8wn3A!lY6r*IkP2O25TA zHdykYkFQ}rXrHA0IqEx|7awS)D2uixSzC0MF1RUE`{&m>mxjsP)#1=xP(nYRc7++X{APa+VMZsVvCqa%_K=3%4%M)(Tf@ zB66?!7#VAc$emO3S)_!qs2EqO(RnpY&jyBvL$+etKd#*OGQ{+MtDz6TvtJMES zq(PM;EF`=b*QNFw2Mqfz`G=q7X`Jj2z5rdcBCIRY_8oii`;=cFIa(f8a}+=3FPs}wG}t3db77DK=w=et z5kldlAAwjoR8LNyTF$=wEMmUwzjMl%xx@O(yylWxh(71(lD++LvdU6V+mQ4~pLids zV*7Jsy<|xL~eBdK8bYbu+m#S5X= z`datO#D?##ap2Oln7Y?%rwom?&tzF;t&}Bl2y}AIB{J;7-(s+UlZx)0SmG%pi z@U!Qd*}so+c)_jrsrwDKr+H08)l*cZq_FcN$3S651>t7#b`yfEF!&9m;l#c0^xeZ9 zyrh*@dY@X?l^=gVu|-`>tEEWNqnYD*^&J8~aqjgEt2Z9OCu@swU!)=D>Ig_DpJzZE zdTs|^P3F_}jCYJGq;_7jW?sG~RpS}WmicIZ z!vqDY?wez$DY0Y9K%9pPwpQTOPSYKk!L5Ab^PD3x{>j92$&E-C`I}!N#uRap6fj_) z8mlos9fv8xApym7UW0gcrA?>-RW(y@_{a9`dlxf;Z?hCyC0}HN>sHncQ($!_NW>EsFkBtc-8-8& z_W?8#4Pa^jU~1CHdXe@)wRcDAD6Oqgy21%jKl~#~h1tW!-qF9b%q|1L5|td6^pTWU zmBOx?)qY@NbG%x&j3^EHnvBKVn?_8V3rLJ!JhMa`OW_i4{oM4Hq@ah(w zJT0kd#)?jk!%eTil@qUKXZ`rOXQ1K1w&aAV^vp|d5tg-kE`Wyd7R&wJRu>Os%X1GB}h*_EmRz72jaQv>^Oan)G8dM%S<^nt;m zM{vRM<6U!0sk>8|wRl#ia}I8(H-b6ybk22%dHtr^V)b`xF3 zDSLR9CX ziO6!*-hK17pLhUn%M}*$ReSTmC|qpz`VGLKW+Rzi3f+;89rqdt**odZ8EH`YMebIo zmlv*%%)qN1>$mXSw+5$B%a_UEgzwl?Izc#(VU(@&&#gkvKK-cqn2!wuC1pW9oGCu; zg~UPcUK^emr)7V1&*pnh9UL2Z;sZCIuNX_YsAdgAM(1kdy{PUMMVsiymughM>eihW znqyQ-^V}wc%bCF@BW)2ADIvCS$ROFL=L&5|AWM&*#?h_!N*7WL5d>3YUU_Q z_&FNhVUGj%6{2`;AHF@e3gRkw&Uv8XoEV&2U##)g{7z(xlC6I1vE(sH^qrKzS-(4G z^^32;!g?O|L<-Jao?V5vu1PaAfg3vk@lW6>GZj&U1FogsIg%F*L4ccTvY94wT~ zH=4?q91s;JsOb+0=gB%L*AT?Ga!phcTUjec-x>KhnLHuk04z9dMZ}Cvk-@dpFs%kx$)NyuZ^;mO}XW`N2vQ{OSl*Mmc>Ur}C{`rwF>wA}* z<%B68W>k~E?zh&z;U6h0GkLUd|JbH*4k4qc4{Qy5$E!r%Ilimp>$g;*vX?9wDjM}7 ztkKV(Oy&`L=tST8E;9A-khiIR@@s6~W9CiC+JH}m{r`x1%dohbCR&u>!QEYh2ZzDkA-KD{yF0<%EjSErg9HdRxVr^+ z3GU>*lkePfZ$0zO-v4ISuCCtI-BoKjwOjtVfeggols%9E81)Q!*F`}9{SV>g{{L)u zclZBX&+lXRip$oiVk#J8gp5dITXQP|DoVE4gmUvc_Ds-om9RC|xivwqGH>3YiAXgJ zEGl}A4A2)cai$g)P%B|OFj;V7+;-e^nhj%{D9ZKC+Vnw=XSo|7*XlDX?i8R>LK=#+ zhEUtbw(j{TP#lnsg=35!MYGN5#_KX%~ss93&a_asu_g!{E zO#@Qsi06e!C60>NJvI^=z-`;A`Tb`mKH?I0VJD}~xYou^os7jB0z=jd0k9%sZ$82dXSzS!h3&pcD5+GA>oJFj ze4!}8v=sZJOi>$$o}M-fyy)l_R;zA{O4>Q5tF0ah;+bMU4l;P z&66#IJv1$y*LlOQfW^6rUK*6R&!Vl8%7?s#;;xrk<#qw~j0CH{pkW%HA=w_(<;F8& z0QO=L;K;FN0Bo^b#1hWXK!bC9dvk+&0||%!Z}{vG?(Pr~?@(hR!kI#zcOK+iMD3vj8ROqw35r4d#&sW2L8mNlMerqhRn;|vJOn#3+C;CNPwz*%cV zkuY~9OB015Qsm-0hrT_2_+y+@)cNx~nt|-425fRHBeY4w0|WygCiu^Uf|@Wwv@!|r zNozbgXf2%;U6J-a;YTMq?};pYd5rJ7I0@$SyL7xa}*NGG%(scv47Cc zrGw8w(2e##)Md(!OgRNe~(fI#KlJ zocM}SSjvBCE_ryisMaq3UhzQI-lz zZV9|LAf-Ab12uWp-si8c{GcQYOb;J z6%r7mNQh}Bn?ZRGW!q^fV`E8_0`yAliUkt^MqyA%&|U_rPCS7+cFl26c4l_u#nkuY z1HuSnhJVReh8wg-XYh&3p&VSA>+}e&NA!LMO#RSG0@N;2;t$jPj-z};wVKmDSY#1( z#YAd<-e6)}vJki&iDGO+>smB$bw&rV<}u5u&4zs#scUEVkCz;vtR{&HJkWHEJ9OPW z(5$B=%Z*tal3pykb(L^sF&ZgjWJwI?bRz8Kg`(p(fJ^qjnsCzbh3jM6bHs7#XHPz= zIjS1Z6W;9U>F=1c3sbFyC+pQWvGtDIm!X-PI=YhEB$IxZgl8Zj+3&cu9nm97sHhGh zAR>w$6@$6P_!#*maW7Jup0LtTvz}?$RH1mCeAco@u#cR@$J}m}_js1`#Y(E{*@@$W zkaIb8R<#9${T0X(DWr+TvJ^|Lxm-1`)3BzFEFFH_Vv!ysm?ywyd2Xe2%cPh1M4Oi0 zmm=5xL2tmB)$vsB^n%*OG$>B`s??v9xV%3(P_A^-o@at81`DGL>ncOyU@l4y6@DvX zW@MpG4-rQ>R26@pn{OuQCmu)Qg30~#{CQlQWWQ{B^})o#oTC+rjv|yC3T~D18sqQR zAoLS;Fwm2jv5xs>LwsbdoQ`CLpYX7I1$tE5GI80i&(D8B2_u#fhF`y-1aFed|1;i;Zsz*WZ3&R{L!6jZD(>^!)Ls}gdn%{^V3UNVf2w&Eu1KqNnqi+ zV&!wkU1^Pdj40aUThH&^FFusq+|Vl{f?B$!?XY{MUwpi?va*Vbic)-iQ|7Tb+>q|J z-FS^+e*$oUT7kOu1aSle1|c_?nVs^vD}XzbuzYrobPt_hvx^uKI}`%{uwcHTuMNEW zV@xx(rJRpZi;gB{FPkL3GSS));E6BAXmpCln3wP~dju48&b0fC)mppak*aNMw{PMS zRe5S27SWaGYEh1!wg4GlqwXi{PYybX{@8h`mS^ z+kOl@GUx5!uQxUOT!kS9i?MSwx|YY+xPjWt97i<+QK}6W>0Y-bu6|+lHp(LVjsqhVO^ZIP&BC-Pif`m<~@KChC&9Jz9r2D9pajH zf7Od+u=JRDO|d$j`+-`|gpPE;4`<%C1Zn#AfH&E4GQj6#1kv!__Uj0_HVx8=*uJ~PxTe@k-T#qO>>~kH=J^94HHOCJrdA32%28$#M{Ja!vUOAb}VwtMeSp*j}gtkKBY9E8A@Uzpbcp`qkigiYdWaDOa*^21oR^Iy{9d_pNJeAOPmGsjYKTblmSXBArLNii+YeLo0%zsO&iW78=q+|V)iILEK%08$MWzw_GKru9M(tAv zsZ7d1p2MNVMGUdIVqt-&TQsyk+Xq3V&+?2?zFo7j%M4|0#Y_FH=Q+D`PNn5K^0Y3d z1(9-g`7qVgcnt2HW?>5z{fbDMmMOD96%+F>T5c;^I1M0{UCZ<}WroEA&?rC#o1~L- z$-~-6pz)o{2j1rqk!$djsmU~Q765}8>rQGt3(a_g0fFEjf$^z?XJ|D->3b6oCAQi1 zMf8cVm9m)4NDG={$rt?q&Ts9?JHSugA(Zw42Pc*&VK#M~<^hkRG0DL{>@Ym1nV^5jvIu4iYS^Tmk8)X{GCD+l*o zw_!n=CgV~^8lTDT;CD2(|DbrS@gJxMR{}JBP-@0OXJ9aRH9{P3fC@7TJ_HtbEe98V z5jTRYNUrwPZAirEjy|N0N%#K7svO>5%oMy0dAYFZL==|0z&i)!i)FqEiBni__eCc0T3upn5|nph87Uu1OuzP z7flyCyMNQExQfFlZ6lLgJM3XHA((ds2Kx0fMDML7H_I;Palj%Ao>pt6W;|}VX3!-r zF4tvKq$2Q+%AvFw7YMp}Z+zhdkpx1^NqVuYl2@!8?vEAF8}Y`6PC%H0G2sy}B{=Os@L#+2 zGd*m&;%jq~Kr|0LXKs3eQK$qLZxOvS*4gO_m+0u|kKg}}w$eYf9UlM_azH!&yoU*M z-QgMs@=i)@?2C)Bg6%pSV>(&A_I5ETxhw-(nQQwahUPJt+A?-+Iof6@!_df$66-5|g04y#%=QG*yHpqQuqR-`djDw=KW0=&}yoG*)^#&6$!Zfsy!s|mI?4cUo zg^2U^)Yl+yZqpV9)UM#vL*qCZ`fahaoV4nQ<1_MPee2t;gGdS}U11FVKd2*<6}-r< z_Voj#=9M}`I%^tFI?d^cqSW8Z_PKM!oNxzAP|f*eo^DIEUo(cAea@Dx6qurU_F-&e zrn`!nYKMzyqmy}QyO}%AkZiIyT%qU#dMzsQwSS4P7ABmV@8@jPlD94^M8!e-le9^VXZ>-inl0TXa#ShxWN^MKG7 z%*Oih_JoL9v6yEx%g>*)s#IpLgl(44T)oqiVvc_bu=CgOINNY2oUv$G?c|=ovuZyF zZ*mTpT2jL4Y>EGIy&;e^m6rcl@Nv%eAE-K%3>Mz5_Rr# zy6mM}yT=O6#Gkb631@H!oEXfESIC_=!huR!u%ic?WeRVT-q6(OwZrKIwYm^ZN6{&% zVV+zkjF!l;y0^wl#r}>*^jtd*W6u+h0pTb56DM!o>VSub z@rR9#jp7@WPt+p}$Z1M%T=aE#wbj(ym)vU5@x%8Xt)SVNM|7?FM04^W*qUFC^wzP%rwp|*>)DZr`ouO!)eV--Hxy$n56zXW2H_M2bIJ@Sd?m~{6>OzK@KIV z;j(c|B@}3B8Ip`27H!#s$oo<_cbj!S<{oU{Z+cP@_N5; zSZbdqXOQ4!Tlnsnup(wp#2i}>_u9KWc`>Yp$y|#@ZGTgqi+Z~qQbys93}SE9TBI)K zw4Bsb0BI5r#1&kQLDD4Hh=+4G{WT0DV(fOsHCI6DAB&^UPziPwDV}Hq zj?@7e1I`qqCb#kF5Rvg4jo8%%)hLSS>!hWGz~z;dsGriLuSL2yipvl6Ukym zU{5)!QoV|uA9}-h-+H2lPc~!CUaZ?!8TEmkO2=g7ovZaXJ@qWw67;#d7q;>+c@w0B zp{}=gD7Ohn8xnA~HCa{T$D64~>JF+s=^Iw3-pEh5aMd z>)jS(H9@?|weW51y?~m2a#NZr9RV^uuMZGZ3&da2F)7ve500 z@6L0R{yNQ13)NB3-Sw4!=5QPn*=IFfvv^&A{SPb8)X_@bmuUy#k`+vPR=z4$8%7;G zn^?=FMHVf?Z>L=;HBF0qeEjbuR+n$uQTp40^^K1TxLGO!2g#!lO|CzYHpA=@Jcy?f z`Cr$-Be>S+)>zg!UrGu|gh}37`q8W`xLGBRW`F&{5PB=FZiZUn{%!fI4Q@->%H|oS z@e_{-!n-~zldY&u^o1}qd7J0xkN$E!o{zq5^$) z7UL?;w){PvkHk`h6u62jhi^Sj)Te0YCphao87W$7j>cdYK$_Q8Gep{({3t&Mrorob zQwQwx4Ut*aL|b+so&r?<9PpIpB#kC6!5Lk*D7PWEafJ=iyv68?8L5ObcG!m@oa&OT z!4II5;-tjM>pQfzfdl4NIxu=n0;3V8vu#kXPI?x3O5BqV;UK4oWx!6FoTU@o z!D1@M%~P^>D$ukHk~nm{AG_wS&XU}M@U$$N$!VsrIebdZv%wrX`q*drSJhKspOKwg zF3Iu@rATdO61{U9JPVfoA@c)={eWPs^3nU<^i{$}dyW3o@XlQ5=1L*$#x0X*U8}k; zEx?tA9K`|RzUZ`}Y36)!GFmkDF@noU$4}aGXV*6uK>{!MIf~%^W`{g@bIx)ptiAo9 zUz#yil9*o+66nN_{x{z(elXLm}N zG<C_25g>89ST4eu0QNoEOC>r*oK@9!&k1c;D%^S3d9Xv&T z(G7JaAk&lC5)ZxlW)Ct-~2gRb3t9*EFw z8|d(NbYnN$RUPO)8A$AIfM)(BP5EoQq{Wvq_9C{B<%riCXshcot;Pd&Cb*?rofwd^A#3D3sah1N0MM@ z2vb>1erUty|b_gMy%UW204S)*z?3?kvo#rxW z6Cw9K*4n)*km@H1YK^+(i2N5xVHPoV?=!ke6*u*G#{+Jk_xYp#*l~x1hYf_#q*SU%Rh`n>Fc%7}m4XHMg8_uhs9@agYBf6@| z{l{byXHn7Snb3&2xoRCj1h=>E(aR#6)K?dec+5vT)Q2e0zJ}M;9jtOx4-s%;X6=&m zBh6t8fS`C*;NIv}(0g2|u4*byTtq1SXNy3U$-+N_7EjVv>LkIk%4FvO&vY0cB*?GWKxdD~L9 zW|0Oke*H0L_7&KjXv;tSN-CDyDeM4SNn_U+)uuk&+0O0X_n2$IOkSUNZOZ?k;*Vf5kD>@RcEF%X&rCUyZ*;e@(88 zd`%9D3f}t;gjcf#`N$wi9Rx%WKILh$tmSOx|ZHOvdbOPu^Y~776Do;fwYk{)pafO&Fo@rG)5limp8HzfST9 zl7_x-*l}84ynTd3iD8R9vlzbj6hih_9rKPQ&q&SdKS0ji$s)=BLCp$yTnZSRT)#CW zE>Kduc+>psJe~GPouElWnOvoWZVikv$!O-j*{UpEu(_{#kWb@VBpY z?&RK2NVrG^xN!F17f6MPUM3EK2U(bWhQnp=V_x3PUyD)+jJxb*9q~95(L-fa8@&|b z^D>>CQc}Y>p~I6JW8sUVN*5qEu#lTFsq^cb%k7zsN8veTWzMcwN_>uA&3dbFR*N{q+3t)y zU&=?4ka`VWmM*)pVEXc;R zh8x!4f$@d?u?HH~42>K2rq+o4X%$rnIccnW1syw#-bUIs9&i7Vd_DHU=UrSqggPJmmvq zAQq`qLXf-${=YZbJwb3iv8)_~2}x~_2_Pc?g6K8?eU3=P|BBeyX66XoIm5=uaj9h? zlkS!%smna&2 zO$E^TV5Q?KA_XGkIQA(@*EC-0G{6!DMN1dO5oq?)MNFB`ZpsrDL-f=2A=Sm`ghjOL z-O1i9X6cI$clGwt#^m9W(|8f`?t`CsWQrs;O{cdMyL~AdsrFjtXs5kqp5vV3lj2E% z{yf$HMjT@FJmr1N|DY{W{=?Xy4FL;5ZB$Jh{LP6pt^QKI^6Bqdvo%)JoOQg+kwnxa zpBw3SKcEib4)rxOlqK6Ot*^i0(OBox^!G(TrOtV`!8xf!g@@9<^JQSv#3A$F8OSWf z#(GeCv*vXm>sDG${fYZ;!)$2Q?sP(1?NWon;*t4guEh|x zy0#ffzQ9)=UgV`Z*Zowf=y^|HUotC=0H=*l7AXYl;HDoxfZZ`K0qvZO7V=<;5=X#j z&F9)5k+b~1Tut0zkeHG>e|Y^c>NAcGv5x+~7HUdbi@LgJ5-)RpjcOmDXqpngH^`T0 zTib6Hh}_JNkKpT#Ie->A2ar)b?Uq}Izv+Ao1RFyz{LlX7Cspl6(I>?JBH*_GR^46srup@~WzW1@w;`5wzd?1|Y6m`e4mYr#xt$qH{ zf81pQg|=NwrPWSomC@{yo*aA^gYP@M{Cj`hp4~MGQat^)O>Pxv>6sh0^>_ZEC}q}#k!Q`V z)oBaZqEO_^6qq~}GwQsCH8#7Idg)6wgw$DU)%tyJ$LeSpg;$1m?| zSxUh!K(p#no#nLel0CJ9nP4-q5f(9mZH`=~6uW6xiJZA&?Kt~vVsZ5km{Rlg{1NGs zZR`>cX>5GpB2_s`X75a6*bLMtb<<~4hk@F)Yulmqk}BXsk+EK(!KVZ=7R2h|+M1@D zgKy6!n;H&!KfP$?fj^8CyHF@7a>h>TlKf8`dW^gnHy!4 z7k1dv>n~}uF0uU{Y++YHwOFgHs!{L}Dko#G{mW}&IslXcsV@OoW3--#xxKZlj#7qj z&0}UWQB@s2&j+r}Q;ugFGY8S>{bM`JmW_WuHjfGwU`+%#R=U>$r3m5%g;FrYLQ}bm zrM`p_F6SAoSJZe!P1F&q=TJxe)QojuaW`1uY6P`V-+wg-$SR(T9c!ZtSsR@#C@)Xr zhz!{E!M->}g2e$UWYu1Qv}J1#%qRZL!3kV=Ihg9sqXRIkq}-l)?}OM{n$% zrts4K46;tpv1kw>dPWj_o)A{&U5#DjSnpI@D5PU6G=!I@vOssAfQTy9a4;QckM}fh zJwNuba^+aw>a~$ho?{PlIsl;nLfDlogY=jI6Rk)7H=Bc66G8rXQ6bnE5_?)Ix$eXT z;1VTCah@gj_8inDWS6~3gQ|TIwM80nPWsTtU~04$elkapmPb=>v#Ik;;D~<4?MDvR z-aJyF?kJyCPQgN)%kOH-elhxuayMSCq_kJ|DM|wk&6eilRnOPNGM3n|ken{IgeNJD z!z%BcN<6M+2ycB~X^$YvOzfhwrCn{D8i=rMa?*9PGmAB>kJT(J?@SJPbu>_#VBLrY zNM3Kl_=|9N|EzZgXcRJKB|Dl#a3EGVT4JwtnLAz1yqxEqBsPxCrOzXz!ql{`73@S* ziL5+0p&wGt4CQ6_1??9XdTL=|*so_hMz~QW=_@*>*xX?!hk#Z~7SOSd<7D=WLD(W) zuRr_OGef7kpLISFPNuL$EDM0d&#ra5YEiT)EZV*KFIQt;Qj@yu^tcbDEvT!GYEAMw z|3OLI(bOu5>1!4_Tog$@ROQ0&)m-cYey5TuXxChcI6V&6Tmez*7XS<_qPl8#aibzu z(nl9MtFfCmM;p11t-eZSvhB-sjWYj=U?k|T3o+kb~-q*9l!ZIQNt`fJVGlOxyh{-)(f;U6= z?9SJ>j@1(W@u>SHa$1Tf(vU|lQHSM7_d9wbggB;-ots-<(X-M)A^%cKbn@wUW_gU4 z(`_B89Nia%Pbn3XXZ+Q_7hB-^eZmO66g;+>;T|%;%kJS8x%5aQ9M8G$=?CWiI0#7z z+zjUChGQ4s%n6Apov-oc@Koa=0L?PQCvk2n-g8sF@p0a42znWbI1@*&tY-FJw zpo(E`G$T@8W3sZTJVSa-|G_NBS{L^vH+<$e1Z6VsH|qW1t3MiN*R-yRus3rO95GXi zZ~I;xX064jT27*34RlU8*;AkuU>{%-X2LdTk>E+fijI{FIa@Lt4)liEKJw*TOWLj3 zyEK8&_+?g9jHlWg5`nnfPNs0aD&f>Hke#~&ro0rlKkJ;HY!>R2It9=bI6_$SRZ?y? z-?s>Ef0U|cw+Q=bng{0P*w)>!*h>hxCWqQ0MqtA_npmH&)w27o+EwgW~oJeG;C2FdQn8sN#W%t3LT~GyTkp+B+$`jGc7gL`@l*+sGpMR- zkmz|(Nq#=NEC0RlvQr>L(&`e?kc-ICSgeT$wj&}a=<3G@&OL&!M}&qPI^Dz z?Mt!it25xWPzA26g!OW1mV^s4&G9c;Zs;yf&^rCZ-y5=Q8n)aZ&S*NACXTIcc2~YV z9d9D_a%}Hz+L~e7=_b6?jxw_k@K>>WZ0u2yu4|$;Kz9*0OYp8oyuGr-LKxkft*Lw$ za+Mm_uJ<|`wj!17Fn`iDl3O?C>PAQyqgD+1fsvF@!1Wj-$sD46?>8afMJ>+w@{N0& z#Cx|I+_kNNH-2dvd^GMkz08@}IT&q~6V*;%|b zG`;M`eg4C-LGvVhM^5yIs-rX(2u)7<_Tp&FQgPPYBV7mlyl4^)_b#Qc)R(d6T;R1xTI-wq|ol?b6dxfydQ0yIg;E|M0^FPZCZ7Ba^Czo z`l0kq0r$ulKj@rg*p3)$J1>|w_)3Mwi`oJ+Dp_$L7sVyTRcdS@BPEuGSZN<2vv%>$ zmK&54su6~S$XtD8m`p*C8^bWt=WCHBx*m0QIT8fz66#!oZ0he&r3(g|93}*)0j#a_ zy>6pqt7@VeLu{f?Ev+x?H+u#hR#K_LKJ8SK(k#r_UM?i5-?H?;#gJmfJf$c=0kQQ$ zlv%u~7Al*hX9eB0q=-7|=t<*r6auB!SvpZgcVV;kYJ6duDbuCBGMdE8UjuZmfHmk1sQ2W>1lQAvjfMc}S$5B5?T_Yo$262-{)MRfrx zdB#aq4*0fmUC%r(rB7zO`1M~p^?@_Crdcem5xzhUOE)%#iz&oPx_9EIi;?017zs>C zyk{T1?=ORlNU*E|*NYF(5cf8->uPKyPOi9_+jLB~Xo(-H394xt!bh6gBUO0QEEQUl zF>!l~pJeCKkk%q2*|JR8PUSM;7HDRmFvMWMkdj+bnpo@Ue> z#^>aZS{1Z^rW4>cRgT(C`j|P4bWheC;&cV_nyt{5kRer&{E=Q|! zYqb#|?YuaY-~6psI~?EXa~EacF#2$~63~U$es5rz7B4J&F1rFf$RwaVR?-G{JpRa2 z=fkiPg(pe?V{S?s12Z-+tYdzbkeTdu)p+eot(7rsadzyQ>1H+Xg<&EuQ^1kY&504N zq5g7jP;!IB(TVtSGBVk@9qBUZ>n*0_P1ddKWgYFnLGM=8-Gm{~O1HKPLBh^@A)`Gq z`}g8lk^L4S>UM1M15QUhn&cawoHg)If!SlDlSZIX}9?D|&q=G2dv6 z(2w>d`cHfMd&GN0I>f8+VVIDLU3z*5;*k6w`jp{1KQ}$OLuvUtCH~Z$S!On^FvKl5 zTHNXdWR9@5j2{)6Q;+SnivK}LV8=pC<<%-osI~35Sk-VhsYaExA0ei;fb6>ODW>}N ze-`|^L9&3kYXPYDjde)WCGGCi@7B6At9X;?mHc?p^s=wPd|omrQR$RXp3Wl3v(R^h zUlG`+t54Te2yCoJ7Gc3+p&XzoaO*hj8a~%Pj``ZmF}FRl$%?#p#~OEMcXT>zE&jHR zGU9!Qy1<)IDN8c^wWI-~(m>8#Q;u&gBTe=>Dr67w9mY@aes1j-MVN~S1s8@G6!o?G z!e72KhU#&+Mv1oXg?Gh*j`!vVb&I%=NwgF7VET5}R&tny(V!)M^3*s(o2pwQzJZz? zc`)okhx&E8=Hk(^J*Mu&fifgjA#R(dA+^?&?=FRS-AvHkF8#W1%U?a?E*7|<1y(D0 z(Pfw{Y%3UqU7s$NE$cMoOp(BSe)i;!e6$7-fC^&0wJ^6~?|Yq$esSp!9w6t9$U9m} z8L5hZLR@M2|NeTWcA(10rA4l7!u6VSM7#suw2aUXM|0;GCuFGW!9`gPX>;Ka3fOn? zf^}B2Fp?Q6ORRbmARq{_J$KKb6vtE*z7rh^><43z{8)V%2cc6igx zq?x|_?t5X=TysaOdpK8%xmIXG+E_p_Y>uCEil_SpX}wmuPwac9)P~!m0k8*q$1@D6 z1k~p7S7)e}*n6(rj$)Gt{F{)1N@u(yBS`1*k(gtHr+mw1OA8G5FwCLY&qy&Y>>0Pn zI$Gb>ErUNGSU30`mP4j4jlVi3dI|dtwZ8ZKw>I&+70(r6YTfkwQ!Z%LOqQiQ`L|@x z%ylwny|uV|p4}Ys%ieU>^MF`xjy z4d$Sjif6rT*Oo9-2(|xa1&B<1C^}bN z^zyLKTUc!N9DI-E;&p2NI#qD<+V}CpAJ@BDDfzuwOT-u8pD^!!+U7&>H50MbGh|Ln zmr*=te&O7j{!!vkQ@@bP&$$%f-+9IFTZyihl#dQXakYg{k|xpI+Jx%4@-~ZQoU1u+ zmaZ@ma(mEG1Xk2AOe8U?^m)kXislyP*T+y{Xhw@c_CN84Y5eKQL?G1`=(-70J}{qf zwxd`w>x|=O{tOXI3xD&l%#~mIvai&jdl6Wf=F&@yy2-O<*g3c0gJ!FhYi)6-_tb22 z8QAAvyW9lVanU}+v&MPk@yOgHf#Lv_MXYj=Vk4>P4L-PoVHmO~37lKStCssAKtI6b z2S~Xb7Mlfxoskc@_6s4u>Ki+wp`*?flvn$IUaz?Wn6pk29-B6J3B1H>rv1kI9{E$N zU<=DhEFbLbILM{3TQT3J=U6n_JQIn{^Woa^lnz43v-E<>Qpj!GUry~o<4jkq z@BWCAO?`oi$E*PJoPm?F;?Obhs`xkKm4pNts;zrvkaTSe<5i_V#8t}feSv88wz=f# zRK4ddYa6buXaxo`QxvO3HIJv9FhB-GsDIHlepDTK;hv&}gNU?uJmQ?V4F@0fc%dE< zgF>SoM9?-DRD-N&H9wAZxw+6}ax5n!EUQp}%?z;gTqJ}4=9Ml7>$c#w z>jgYmSWKnL0XL$~>*5*~d0Wo48;E5F;}iXBiR%v71@_VluGg2O0n8R(-NMbXTLGhSH;nI&3Y4a)>$16ya70b_Wj4sd|6Th8MXMf#{Rmla>Q|c2~QJ} z=g7kHpc~CkRt^H%=opzMw8a3|Ki^QLPdOtL%6M}70~_1oQTODLiW0f_saojm3QR@-TE!FiHxjA+f4A)!6&ysSN)yF5jxnd7IhOH&3?dQ~MP8i3p zsX->f2&WsMxbP8&Z(#*$`gmjpMM8Niyagr{&xWd#?X+Ww+=rCyx9p{^Lta3gj-LGH zp>N76=<374WJd;?2KpxJnUii)R%z!P2}pC`+)(GCM4Ft8jEM{KTooqFEKmp#-68=B5?aWqZp|?VPHDlM##13y3u0mE&>N@yI2| z+L}HRD5j}oZeD6krDLAPv;8@`V)hzR@9y#IuRTglE3#Skb(Z^QsvK>#K6tqd?K_PZ zYTS4_u0L@|T}aJc-Cm+zHx~#o&Piq@g#zX!WlhdbfxMG%+Fnj>_`Olo6#1jirl#u5 z-@d8kDMvBq&d1)mX>pqvO#~=3Gwexfgz3=L(du$4v5ladwAdYMHW+O>{Q~eFaO$bWck-;Vq;<*>fA5M$B6xV!y~EiXt-XEC!dSa zT72SG*o<6xg1MQXB#yFKuv%kkqJhFKWwRf4VW6Et1eQ;Bh5PhOjKYQa+FWN9&XNet zK0Eu9f2?crxUW|Jj0mMh5LMG>NMCv`HIn>ajg4T=8!lf%7F9aKnCcPKSmUU7kv;uv z(}M@jl94Kg5A@>oVEt2`>I>98_tBVC=Iw??2(bF zBZeW;%)Enx%T$WW+rftlco#f7_l~E(KnqLHeb3t*Ouia18&08c&P=4{9=z z2~u4ZBEy`D#YwjaMr2GT6E|F2ca ztkv^st8X+hz|*dXiH;}IZwpwW6UnES@surj*9)TxYa zSZ0wty21uMNZ_a?NswKN^6U!RC39-yY{!=z^1P>}rJBi9`^ziAo9I!`dORFHnm9Ui zxc@(>y|rHqH_KZ8L4BL~^AtyJe(_P%@)+3wdi=ZE`6T>j>#Z;NmOSM>WgcLyn%lA_ zK<#{2RQUdKK%YV;>&yEg@*#KV=?hnfYM955(W{`+gF61HmTL2^hkX_xccEAHjHbHa zU>mk>oA^~8!gIBe8~{5AZa>|4_H(1jVjwcXAV6UshVf^YBv>?%^GLI?ak>=RuGZxQ z@rux)u#Yg_zd8}JVT312d_ys*@jhf@@9<8@B6l@zJ*{-CxmbpUJV;@Ar=J z-%!F->08z=9{mO1!~V7O;KqOOq~eEKM~B8YHcBNKOMeh;w%rQLe3c$XG11Mn?r4=P zOR4ou$dKePaOCK>V3*q@+As z?@EM{246dEZk-)`d;>0#Q7<;^uY_aCm!r8fMDkJYs>4b>Pas zJ25}`e4`sO4VV_3q_g5_BtP`DP8q~Ami5g-ure9I!Lu{jKB#<|qeWi*)MY+wo{h(zG zMRlR|Izr<#>pM9Le0O#k+>aF0oECj67w8CyG~?+01s{M4g6z(zQ4mzwD)gU^6+n<> z+(dAG0Nhv-wBKRQSC6<5mxsk^uczJ&;vV6{0^^#B?u>;XwyYqu%ng=}x=;*K%TYt3 zh1swnLmJQrDe;KMm6eW|e&BC~BoB%(a!R3fbdcZC*fM}_x8>^5B`#1Re7y9uP(o|& zd2~98DGd^TUbqWRTD5Z;(loi&LgE}$b+qjJI4&)YJmGgOG*QeOP#jr1_O1+FegJ3v zvRRL-KD<~+MFW+WVNx0A-&^Gsp@j#TICuZ{7dDMc=i#~mKq|T#r)gH)gK|_QI3rK} z-WzXASv_MxiPc%tBZ+svXz5ogx;mrf6mh2}2Y|uVT|QrL``+6+VbW6waNB0=)MiAE z2&uEC-cv1WFMOgPCNCCcI^awdJ?o9H_P&z+X&Oxz(XiDAp21u=LzSyFa9?D;ShAX2 zKFEcCE6zvUt4u*Bi4K$awZD6@tLj?ix`rQo?`uFRP~dQW;pt^mdh96?<5t)tMq8L= zch)O0y9!t;?gu|3Xm*4M(mj294tz!G$u}yyA@iIN1`sl~grfzHI!etm8IxL8EkxVb zWkBy~mperSGUa$il{pW$FmwrQ29Q0UUIE_nfXWWN%wRf@qY!@;Xn< zpJ?m1Kp0jl-nn;HMos;CS zuF6ehVQXDi1wC$2%*)2f`ZDA^_;TZinJ@s)0 zzq2-*Qqu%~z|y*(cITUCB2q&sJ=JM6%IU-T=DB|CbQdULq}6vgWB!EfukjKged&Vc z?76^ZY7o%Gfwgsy_50M6dxueski+xoz}cbRyjq55Y|TkVFHl3bGqVlq6dzANhu2RA zLDq*2Z{u_rEiahqB=A!{r?k#Ezj3^i?ie9?^!r!vJMV5HMBeQvs35oBKzk?su_J)= zYvsI^M(kt(jb{#o7(k^8nXZ`~7f-uC;R|%l4gzU~7=O&8 zu?%KrN)o8H-23w|dHmjf*e-vH+-_&FJpHYA9>tzPm^+!d52EZ$D5HRR=b!qk%ah_0 zti<1N@ify?-R53PVqO)nows?Wf^udfxSe)(fyQPuqRcOHK4ET%lU9#s+x}_UWW=>w zu@p4C^sTc!$czSi%wCWD5y?P?5qs9+->=VmMR6WyXb1}^o}*30r)VN7L5PmV*WFle z&a1)H!Axmcec5so76(bIp3Q$D#Gn%!r_NYApYP=CWoif@q?SjTWdQP<6~a5Xk2 zyFOM>)~WhQMS~9Xsg<)1khMT+3Cx-=Ql+xH85`7a%ksNG;dR}y;-34%C4?2g)u6Iu zAjB$1FBt?#VN*QJ$wfKIufCj5D!F5AkQk|Wq0aPZyl;<|^#LJ0_l?->1joD_uBT;+ z6!cYbJYX4>6#D)LW$;(@Lp}H^?_d5DF4DLC=S_SS^g&MBFnaF(Fx@EuXqss|K7Mxo z%VMNbG}WhUyy%qccJ*U|_R>irn32$@=Rc_EI@1LsxzeQ%N7h^y+G&S-dD9~?-pqDp z-?JCqinnugRtXI2t_iV zH;K10NpU^;5PMts(Cn6DtpX@=;mNK2(2{qOz@t zByi`r`q2^#V+`%w@>AD;QbZOA`%+(2axDCjeIQiGYp zcN`~TcZI4~zmCiqWmZ#`j&WoX>lpctzcZNjNjRuCOK^QPx#{M3`I}Ps)?shvK&-3j zkijL{%beP+RiFMNc+MUk9x?m*gvo_o17u44J>g8Bnn~3($-DsX)0wuznY#6t*-xvm zn?hHNe~_b`BJ;w@4cPYZ{?YtpEt&V`ZMBrs%lg=w8mWWM?i1-n4MC4ADf4W@lOwNr z{f5|g`6}kL=1LF8)+fwgHRW{-rNnbO}VkdN}Ep*ZoaUq4F3=gYMY z)-U|cl&f1n_wh%gPt6#VH3+UhHBVVuR$eMe@iw?Q;W-*V z{0Cr!nJa2F&Oh{f2kH$=1vS9K@g&|41$}Uynm9ON*wD?bS@6-ZD4UwH+Y>S-U-C}* z4A%{(c6}>(5gswU0Nbs5be+y=YuJ`Hn};?m3ryTX-s{%SOp#=~XCBTiY}qSbH2b;H z5G&B*mvH@2P$1&hs)_cj`wdffFg~R98CQXGZkAzJ*+0PiVx-~qhFlE#G#-50X~(gQ zn?~2Y)2X|kmbaOCy;*yGftBQhG$%C~J1zNC$$p#(IzH(4X5^okN)=Q7OB3*VtX6D) zx@>CE>9JB=-~OQ0ZA^j*0sy$WvJHk0r9auE$20RyD3qiPOkd}_vszV-Nl!|7>&;mDhlW2( z!AC`KBk_=N;gk4HkGm09MY_}?@Mxc7nIo3Qj0XGRFj!3H4O?ZNbj z9k&ttJfYB!Et~}xYU!gJ>t>Fq7BK0(;4_9KVR`Cvy+1l-iu9hfKF_2`u;({9yDCYh zj-}=Xs^k7r=JUs|7g)IcEqRHq4orWhTXEpN@!9?$o=*Q**EMxeh9*};&}}U#eeR@Sa_V)6{2RC#x^!1+ZGIbUCm&{>{G-#$$fa$UDSG- zC$lCt8c7leT3dYD!WVtF2Bpu&(v60b7pG*9Sw2TEOe|#cvwdkrx;UIzyx5yVMC%Z@! zSy#MDe9e0qQt^3RH{}f?%I9L=_F^}DrS|Np$WZ};#wJakBLu-p@}$6_p)n+{TgZ~g zhQgt=;41&9uW}2C;<&HXjL5s}+M`>4|Az1K7!^)+j0z_<5E~a44;v2;3mfNuv^X&; zoaDF^EM%0df{J)-dRCrb2)mGyzJRa-6^GZG&=gK(UF+iJFnkdeYEkb&OPkd2|5NG2 zmIru9)8P%xQx4|Ilp0+#r;o%isPk!hjqH~Wj9_4nXVe?gFSuML}bIh_s zZ@maH-m{>cOonZ1^CoAJ{j-$FE%yw%Xk3XrqiR)ZNqKEBFhpsQ$T;i2HhxItv7;`( z*9?4wq+Cs4c7hn#R(|ULN7qF+ge^}R!qe3py<#0Vi82-qhByAjwyYh%Nj>@(_5IYXDJ z)R##~3Z;362}L|-(nF5FK_y?d_?p8k>H=-}&8>m_R<2qAKB9e}^#GjZ9^vG8;9Siy z+eB{|MJldz9u=?Kgh)cAoa6;cp(zK{`6yYbTl~aTUz$rX+afEa>f`iT?U!5UlKj+x z(&UC)3qV(6w0q41ULb@c@DfO$Trb5-$0bMU`#qb$+Pas+77^3$GScZ``B{KXQ>+iB^XSKvzoL z5tM;dLs$A;T9?(b>+5>*Nk3}4=_QiiQYvzeMyD0uArd#{+j@LVZZ1ASvzM) ziTxpbAT#c&J^e>LDmG}1(*6cZHarBFHKyjT;I}>nVx6MM+av|qU`0y{zZ8=c()0ZX zup2;Z2g;$1K%G!RqtCcgc3EKAEa5SCvqN(e6)&U(Vu)&oXFfjn+#Tj~zqL@~u0`RMoLj(Z;LKE6sYpIzb4ev*(>t$2=8`-RdZaO;_pq^%(9$VAxmFl13{y#4c1j6n50o+$qKedkZ; z=nI+jyuAPLewv%{8d%9qAuWgeZBORQV$G61dRDQW5L~W2Y|>#29#zFkfC}S3N^{Y$ zpk$~;Xi7)IomRGL3uL!ndcOr9ho)A=E2N62nY?s9e`~p zkA%EgK7u{&shV+oq4EHm8IG=$Q(@pX&rPFYweK{$Yr%G@MpXcY($?Kf(ezg zRA*e|N(?WaiC}-8L8Qzun(fKfaC*B{eN5SV!=del^NRX~3CML_w@_El^`nQvtQe9SQI>>`tH+oaHU*gP(VI>L;GX__(5l%JfE4{sWGvsTcI7yT>4 zXr*pau|2be9K)D-ECE2Hhd63p6u&W{g8$6qD$J|uZ`YbB)^B=e46l0xRyuIvQU-_$ zcsVLN5|-A)Lru|8(_73PB3YGFlAxa4q2k2xEZQtxrd13{I47b+r{QAI&8?C0-Nf_s z(crMEb;Ns1QRDg}3q#I$o6%sts7%ctRgE)M%M|jIPgT%@41Nbw2*Z#LovB+ z`Et1*DwSZJL59=r6>(eed#C0pG;dQ#BXH3V*KgmGA4e%*k@^#&ihz!NXGcMYoOkig z43Jt+t`l_UR+cW45>?GzxdWO6ZJX}{0A|qJy;8bcWNcyv3NuF{6<}r}!Xfc{RQO0C zK|yh*Rmt0HpG~1bl*UA2MDkhjBUwa~TYfe4nDCXK888|#yhb1Jthme=j<%pBu>|GMjck-yrNI_ETa+Ck{%O|99fOiZC&Xz6 zYxt>5CG|sx6(4l(prw7X>N7g0_$0+rw931__2LAER$AGgOkFQoThNT*th>-78er?3 zG#}5LCm)S3qFh?KeY1fv=X^^${V`%?rxP^KM+3Sf3;v)iLii;ai^oqok}KV83){-8 zA=!D@ls!{F&cVx3_tv>G=>rgzmjkvb()&}aCMkl^zI4=P7>^Iu;QXV(@;}QTmuzqE z%kC(5UJc_d_r}z|%ui31za(0tFbW_)v}>$J-hQt`c!gCDT152^K$fP&?xlh$$`0wt zQudNd10A(M0vKp1v9?`UE%!5JAdX_q@t+X^C@k=ETs4?{$Vlr80BU+?c z2g;6xpsE$T78C0T#qlzimhw=DAniTgu*YS=FfE2@6}c;v67QzmV$XZRTR!5_)4XqMK6Hj19KGU z{QMG-Pa$~m8hz3l)fVDPsrHPPEkx>c&C_+MiBvoG(=)T|4^~AKrbc6FI1ma7iE;?3 zLW`!+YwQ#|&F}N#=4NooiXBDoU9Iob5)oe1rNj`cUwgE%#6cow2fKc0Q=i$AOo_n6 z27}a1$cP^)*SQhOgN7+lQ~KfrVaCRD&rBILa#|V+a%vPHq&*hrM0%+?Pd>h7-DlGc zt8UOvRZ`ar&I8Re=HYstHJP7K)0GGx_~PQpv` zP{OD1=|j5m@cn39d_!EF??q74G0Z*{74ar;CG69P4_3s)S4pm@7!wLcxJ|>DMimd{ zk28CHkNYq;mg^Sxk`Lc*)7OqjIP4=>4}I2Uh;!WhcZ6$57@^O0ybE$F*O`r0v7NezEi$T@y>Y}aXj|E1Q@ zK~{V2>9kdqAkJ7*6{*={l89`tFn`L|e*o^`>(&%V$)Mhj(wiz#bbA%4Bld?x%b5Jq z@&&ELROO+i%t8oR73Ja!^i};iLu=>JKo-xFh@Z}sXq_z)DKJuv9a}+#+uSHzfbV_&W&N`6UK8UieRp@v6+B{hxv2|~`yG{4TI{y!*(B~Igo(O%-L89?` zpPtm(@}nB{>Af9^@yfJ~ZN1@fNHua?pn7#IRB2ipBo8QM$e~nM5d-8%Rgl=JNTDd- zVPcFfc3%R&<=`paKxqDL7w#H0m=OoRpJkTRNo2i_D}R@lLcvIF2urR(-KEOF{vB>#IUQJJ5ke4U%&Ng5!BtSM zackixsswnE|ztMSiHQvyYmQj+pf+cYn$tc2w4 zURQR8+8!DZn8_KGq-2P_e0MRlgxsFWgg=zbRb@wijDQW&5Z{94+e92iOjn;2+j{f1 zfBOj;4`-q4dn!nAYF_Tnl_Ev^jc~5fiEvf({O>Z3Rmw26B_j1UeMk&5`R+iv-ocpj z4-kZs;x0`}aWyFxW&hP(%=wfRxL+(WuG+;VRNO(wrTUdA0#{Qntep1$)#C~Qo`m`##wzou4uRr`ro=l?Hu{sX+Q)d7yu838Cw7@jNbg|s+! zhW)`*y-ldTendOWS(G1%qWt!O6KUA7mwMzVXSX#csXMzLtlNChr~wuY z8%Gb4Dd5oiqzqK{XN*cn4tu75j|h$re3otg+1?1&$hVlF- zOjgL&02HI+?CSDzj`qNsP!|`w=(_OPepT^SkYK1XHa941;Kc*{pjlvra**+v;_R9P^rh!_S zwjZE$0Rr74nT#lnF19*B+pu24RcE1~^pnj_nlDrv{6alf4FmmPS5{nC$uAGQw=d=1 z(lcOn7+G=8zkD8*8}8q-cs;^S0lCk<6E8_MP@D{-Rv* znG0S>#ju8%q2={|#FXE;J?Dz*7Vz*z!!P{Ut+3)NHgx97;(Z3yJikwBu9Y(D$9&S& z?WodCs#l-;#x9etoUYm5F5T5zS>Oi!2PkG9@;u5jSeRQw&t_5Tu5RKwBEsnEX3Sn2 z7J<$Tt<0SFy$nm_qCsDadyMk^G~*k70R~#;QQn6wgPLcwd~q7Z<)i;j>Xo0`rh{&z3;!V;bU z#ZH8eR-&|Ry!rkM{reC0t>wScUJ(X#5{0vo*}4hgF!M!*YHJL#lQc6b>Q7It&wHZS z+NTX3Q<7S#=6 z)Qryb{2o1MdI(X$;s;tJ^53@0;HZ(~!0QqsH1#SCCd0y)=n`?JEXyxuwgv4I&l1_zGy4Xp0+TypNpbaB;Z;iaf<4brZ)j{u+yjui$+v7B_rY46djMB?QEBneYaD9#T0=u z{o#jb5^en%JP#q^0WR6{hDlutv$(3&pMZEXf={;`(u2~25;rXwQkoDmqU)jzsn zQ_8Geh6%(!IItxQE1bKrP@ZQvA{a5FqkdV8oQGz&`L_8EEu!h5;itQQ=a6Ob-)Pzp z9!|YDe?Cpbx^I5PH-E9!+MO~?VUFRyG_uLCASU~`wqcdXq%ID(^GSM#r3pgsfqs{_Wcvl56FrUM> zm{Or2P^0-u?@cTYML%DeVGUrx6=t(US~KA7yXW7P=1WUp$;P+-11JPh_EM%KpoYb` zOx@cmDn!-x(CFDPg7?LKwW#ZM81S6oQ2sI5NN% z*DZY%pM1sVJoEdGO)A%x=NL}eg!)EjxLGL3y0LJ`2*{gyjybfp%k1Evez)sh#3Cgd zEtAs+TUit&D6&>asx(=eR~@M~qrH0VUa-t^*9W2CFZep#3{pwX5t^essc~?~db`5l zHI)7amqBR&XEqxRndH=+wxdBz?Cl0;5YG5xNrV9AYDMZ_H|WN4M#!5mfJmH>FHEz; zXT&N~2q3c8^l7iO3pGP>=@*z;Z0PnjuDo*qrGDcLx?|o;a$hQ5j8PpQNqyb| zDCo!GYSA|j27T{H)D%_K*=2CW!8w`%Sa!cj7*p{<8sRpS)rlt{)FGs9(URdpvDp#n zIM3K5bpM@>3xW@eRSq(!%%tA3Qdtz2GV0BURh0<_Ae*tUKouxYF(-z;akxv zwF)_Vvf0Dbh);`tEMzxkz#wgoa!rQJHy^lce6-*2ci25;os15^dY-NSg-c2uqZ3EkBl`a$nYWs));Wka?J3B=8Ho;V}%T_{_ z-Rm6gki{CKPocFi>@tGh6`bi}6)DWBLg~zuVN$V}(hnHxq9Ahh^_K0GU#~19;<2^` z#8%-nU6#5XAC#iK^*WLXF+Gs4nCwg`+6RQGsC>KS#0liqsPbd2F~3chJh(0h=&U{U z*&x%P?bM~la!3Yxce}b$6A3b3hk(8}&Ph!xPWrdU&XBpZdB_Ys=?tC=>iPO4LEB~c zL##9joS1JsNq}(8&OjY|VLvUC!U$+vajBJ9olV&nE+7j&e;X2@FQ<;*nT9=-s1{ag z3k4OP^s6vFzf{d=|L=L{3Ng1~-TNP~AvE@#1#7|mzGZiPq<{X+E4a2q_l{8+6d}9! zP^^>x62j+Y6-^_lFjEDkzeVA_r9t6rA-C23>L~n86Uc=81^B3FFzh{@ET$ndqT6!r zQgN)$s+-|3iDx^r#dTdqCWJM!7a_gPyg^FmPvO_2-vlaE6$Z{x7c0x%d(Z(o&L)#_ z`F6acrAj6(edcmm|B8ylm@1Y_$F`NF9>i13`A!MdZ4ZLTS(dn?vwFb|23D5Q!p+0W%|@Tq zOWGQBe3Zjg7s5jz5laGQRplX@!vv=iL5N)|!*Y*nqlR}=WKaUC z0fSda2i&KjWjJ)RVnMVe%Q`PA1Ck`I$MMSd zOH|VRP1PH>s3(zw14Y}Om+k*o9DP?*kMQzx7K%U`6CwlHJTzK*0L;Is_b@JoFvD{Jt3=g`9Ey5dEO@l7kcbI$|0(q0N-02gOA=YR4` z{4LzqIecPT6MG1j4h}8vY54Z?^xpN<3zf7}E{Zy;zKPfU6EN44Mrq^D59N7)Xkr_0 zB36EXb*RbvqE|MBHlSkb0z5U{NL(-mr_6j``RW&wW zdLS?pqv-}P8$2aN*jt@>->&IaIO@d{lHjgq4r$FYHBmerK8TmhSqQ~L7JVjnXhEf` zU9mtXS*u8znK$7VV#>i+$Fap2as=w1h8x zz%%s^pu~X^89j}oN>ze;2{S+j9?ov$(bB5h5J-NR!Cyw8QkmYc4mlylv zY}JVwv*m7JdB7w+HUO>Dy51jw`_u|cST!vq{%?MXQV3JI-@t}WQBLl*3`Bs|(Aipg za_gMK_?B*Nyy7YD`Gu#oe(oOo(AP1Fjjg`+p{Kmhm>jc*Sk($euRhKBO`DOf_$A0W zRT48r({9EuNU-z)MV`LPV&1UCConAj%~;}=N5VmPmeirY^6>N2)rWjb1>!kwg1i` zudLd_$AUvevdf^x@F$kOfEOsGkFMJ03O=dlO&Fex;Vjc@czJnws=m64WvlVfQNN0X z{+Gi~9JVib$vLWyU6(5cwPi~Mf?ws2p$9l8zpPiK*6DfZOpVD@Rvxy0z&L1z+jJ3# znPL)0P}jDOtK_-u{gRzSwQ3QrAD~rdghW|C&P0B85H;w-NY$XOvYadb64>E&k1{|k zJpDf9=A8HShsZTl<69fo!ory6qLhPd{9!g6p1gI+l}fZ})H0vzZJ57Va~Al09Ox?8 zJ^<4*9kcMO=%C!u5-AIbzopfSB&szsoifRey{VitCF4<;l4Q$8{IF$RN#TX_HgX}qS zdV;rRlwY7zZRmvUs2&!&=1Z11Q5)flYpcg}<=mw}w1NE)zK(el(7!aG?UmUVJYw>E z3k+^qchtZn%5RHO&_OTS>oi6Phh3b?Zo{q+OY zo)P0q(So!F8fVm6F7{ra#r+o&DAfddYt?Q4KcFkYLHCS`2G(ZN!~a z+O)2$a87_Ihyc;)Ww@R3EF7p=0alJqr1heSmuqVXne|>yP5=y*b3B>m+%WAl6>_>r zU^)5Dfy&wX9;TXGn{2D_HX+{GK=T98>GefcV9|Rc-?}L;)(aAI^;g*dP!1OvLr}(5 zROFr?8rUZqJEoM>E_Gb**rOqAtWN7&eO|KTuIk(+Ys3&5qrt|n&JO*y&WU2nyx z;aRdsz3fpEjmdya;8}uE87u@gWQ0>p zy7uE&7pXP=n5h)4fi3`P0?b>DmB#&`;k06^OSMBUY#Pp3JBInAbW5*>g;$bFK5{lH$x_w)1Jw9#SN$h`gRC`+IO7;q*>@h{-*!gSrdOp^ z6ovY5>xgWdGzYPmvG{Q~@l3?2@9hy|`#c&tbn?bS+eZ`a?4xT(HD;*hoFSE5xbGYZ zv5cZ&wF9@#LyaAr?C7rsZ5988`cb^S(~8rq)MVjNQk<-?dF{iOlEIRXO*}g)PqK%@ z7V{}W3u}m@%W>D(ssJaWE%>lY}S2XVn85^FIil&eQ3d+3FTE>mI-|e#;?Bw}k zjA`%}dU@0N9ghs1vZ#vtCvaG!OAU%S=uL&v>pH#C9BaOg!p|i+9B%am?5%i=4ZGML#~niobEY%qNjY4I;tyC_f^^k2p~}t8{UmcA<8!2ZQ6*F4#}1W~`Hf-;xmz zyh%{{dOa*NSAZRNgkxk?NnNy858m|t_^ln%JXdJBsWH;Ma}Ld!RK@v%==T0z5PWQ* znbcLX-iyNwAd0&Bx!43F9e9G1P9)b9@reg|-jh5Yw!K0(6w$zrN^^`x^EQL8p6djt zoD*PdJl@`05vUtB3fVKNKNXui*no$r@H!gjcFBfX>;FvdndNo*2S^9UwUNlD?yoU5 z)xE73rWTng2p2C#C?l`B_|PaN)yj&hxPJgD)XO?ViSX7{1%obJ6?*Me9rB}j>ZcC1 z&3Y?6MQ8^F{(1`*U?-QX%PUdPuPP5!H{XzF?%P!OJNdmpcADJ55gEfWF#LOFDhbp6 z4vc%eRp$^#%kA}tKXsyiC=)c{>9`^B_3UO<)mK?1iGy{o!|r+!=gjO@IA7%LJLW8! zk3KdppP+FD3c-wOvQz~7jkylNly^ioLre8IMqOBFw8)cQRK71%Ays+x%E8(x2xFj>! zgK*)wJor`f|7MPOU(%&0;-kfA0Ni9G=U|9FFr();RP+{Np&B$De*aaP&@jJBW38~ z`LC)K+mNa`Uh9GbbC6T+v|YL2?}AprSFe+u#G$zvX*(pd&9^ zme?xT6E5gF?j7z>U{&+HNfp!Ez^}aR zr~FT6RD0i~7w8G$=5RP+nh!DbTqM!w&~;qj24Fw5?nenz!<5qKX~_X@+6u}uJLX){ zjMD5|U9|M(7BsfP#rokxS{ z=ay+BODqiCM;gCVPu)uWN~@jUV{gzhZ&(-#X$aU#UzM*ilY%zYk)+T3I$Xq$3kyM4 zRHzJDC}6=6xd!4ex$Ei@+EbU8MKGM$-#T&u8N|ZZVn)M<*#fDKr>y~!a$J#UvF{gz zsU$S!lAmB{R3`8t!ecgX4}`HC$! ztK-9v)t?dJ6fDeVDE2`M?k~peMN1muD8E9VAIAIOd2q1v+RL(mR-& zG>-TjBeN29Yi26%Q~cjsSQ=0;XFNS$33c;wyfH@8y^DIOW6p0dh)hnipM^$(3t!+@ zEGTBhK=II)5m_P$+^Q3G81DSO_fmun{T+aQ(1Tx`zfbS@W$*9 z=5#}%{B1UvD2Ni7O_WBc%u2hs)^J0hz$(U=ZD(X@#$jLU#n%MmuObUoX6-NbFs|oM zsEg6D&3U^gFVuWrw>}Y4`}{qDy4Ku-46fgGVA0`84n8}EA60Zr{L!OnsJ&E9hq!S3I7WvK2OWhx$48b-JiEa%Ymm900~o_h5g z3#I+9K(f38;)foyI1ltnQ={O>a%{4kHjBqdsMBz9B=iqm znLJYc!tY+&Ve&s7u>$U}&wB?)(J+kdjgc}=_$)7Vsru}fD$!uxp9;<0BrUr9gpKZ1*Z&qCuQhE3NFpcdVL?CZIW_Qn(;Do`33d| zS`AKm+QtQUF9W7kz~o>PEsPQTZ82vpW9pF}a6yloZ#073vXe(Ky&7wIWeh zeyQJ!kS6$BS5)6+se3D{8OprgXpb*?@MA(aqZn_5>-jEDcmRIWMJv2E9jbDNpYVoL zsDjRT_&ayc?=TnTr+DEiZ08HxZ0Xjnx6P??hPdh7taN$ag!8RQsS(LuUZx-MLdWDX zpJurD88|cK-B*5Me}bkQ6JwAOiAyF!ZKqm%DX}2JT~p!(ZiC)US_}OH{0T6qsX<}~ z)&#yC8F66C=Ap^AB1D7dmPXvQeq!8rnn$h8E6XeV_$-2fPu_;m0TbpN^%>c;-ZN_c z^hZS)Bx`BI@L6Xf1-FaL3mfcECGX>i(+%)~r0O)L?eh+Y$V$Er6)gHTk9m|j9wNAgZq|Ki4Yj6(}hn`TK zEHe#x?7KQWDcM(tYJ)jHCnG(%$wlXi7HVvqyf*NsHGm2Rj5=g@ua4q<5?P)P@D@Ik zW>r8IBmHM_N2&a+q$OzH(G2k|zEE9i;V&nMS^mtdy4kZBBo&aoMDS6CStEyq!!L0~ z9D2wzy+X!!|8}5Zpkdg#gUZdp<_+h!9GfVS!9{g+RW=&>({XPy+W6ZQPDm*&J8#7p zhB&@4N098O`W=;CaQvC%DKpNWD%HJ4fS07Tej?KxLiAK7Y zD#T}HXRoEYx2}G=N(e0j`U>$1Van926AtrG$*1UC@Kv1!@sycwV zs3*x?!XvPb=bB>Z@Iw>?-#)W1Eq^?*h!dVdA1$8qQfP>BHw$gY$=e7%4Q#?sI%>U} zezqq|W6uXt>%9WoSb2BjnEg(UONBS3|_Mvp3X;jB>Ms<9891h3nkj7a|j zV4yxBO*=oN=R_RQX%NoSXjv`z2$@&5e`H2eLEawo4421$TQpIsgB#3d$b>1(y_+5> z{P4VD(RiJ^ij_()s6ovC2VuLus(^l5_lqN^qCp&L?DFUh&OQ}@Z?r}cOft9A+48V1#y&(-W}4OClIH&Itr zZxyxKIb&^~6bDJZBKrqG_)`jVwr8h=L>BX5a_7DY-&_C1@wV7+&n|x8XaYH6cGSt{ z5sG52I)BHz*OYE!z%5RQ<*&}oG#(pGZs<=8Glz2ra@b{7{mg5;a#@(v^Y$3Z^Umvshw)Xs(O&q)> zh(E=k?G{g042B@ZjPnRgD-HENscBP&k>AbZ`48MdzYz<6e&=gkVVqoEQ|(`CTf!eH zl#e}fUbgakTBoWwNJ!{Q0)ZOq(V>$QVSMlfsGySEPV@6_7-O)8ohv`duO+T12|2<1 z(o(WI8y>H$L$|0YNvXS+3E>uY1cxPoZ#s>IC60i>cz{u!7WXKsZ&l(tDPIE?*Lkj! zaYGd6`ggY4B^RG1tY^8-*RyV8y*p!y;}rY)z-jRL3tah>2;&DRc;ES|mnkNT6#^YK zIN&~yrb?wKwUusTui>ce!nHMN_k(r95G8WMWLfZg0`c?jE1IyrvKxanW;neIe-mv2cQ>c#AS4;TZv53+JsM5b} zR&aNcSAq?j0lFkymxy|3quXFTx-sG{-VLuuLW=)W%V2J&0~IRvmQD)P`!QtL)Jt_gvlK*`ee4Yiwwv#NHLi(_h!hi zCGFuPn%FCSJ`tb$72t^#Psy%`Ew>jF6-J)ZvzTYjn5%tkrIv^xfpk=3da?mNPv0;4 zf*G1q5)F6?);Soc{yR{_jOjO)LBA?w$t7oR*y6$68yv4doS--A4yFsty9a z@gAv7b@N7^>|P0vlDWKp(#?gG0PC&D==!mS(HGV2bo!cY;Ni@Y)LbgG-iUc<)fS{* z(vbgv?lRcoGF*54klHqX``*@h1@}8~xii&9yucq1>F>~Fcw%s!9$lYk*Z@Bax2q%y z6#ZkWF7CaIYX?qX7oG~h#P$1@!9$Z7n`YwdviI~?7CF{=5vO_EUD_kkAm7;)# z9P%wVzWN9i$E6pQzzJNCNNAtN+lWRHpR>JcSN2m_gpY@n-on7B@hm;PvVy{wtR%v-=Hk!Tk?NLGT<_3s`BZjhT)xB6 zk}p+JkuRzV@{G8VI(C6}0$#eY9vD~WoVF+ObcNsz-A2Q|){!8z z-)Qdey|guC(Ke+jMa0_vphUhY*@yr-iL;1vnnKxqCa`#@y)5;DF|$0{gyAJFI^j0D z+R7V*)uHP5|J!_oD_3FY!0Pv${uMVo#}|7QeoB>r+2cw{a7qIM0~y&35T_BC)tBA0_0UOPYHd#M>d+k`5Mtu)ziE8IFyuz6nh8XGFotUn;WAcFj2>9vi zC+Y26Fyn71Iq}372nfe~_tcG2WJ1D;+7#ZPEPHcLt%l&p1L+h_1WU}8F~aebAn~Pc z&Q|y!gzL*r!YqsI*NAF^?uzp!CD02F&nBA}>fm!yfN!dW;L`kjYkf@!EogiuT{Ia<`HFJw%N#+EfgGAWqz%_dj?Xb&M}LoD0=x_)fL~_yj__C4|FTlp)WeX>fH8d-db`X}f`GTS_Pvs` z17@wq`{*UsU?v%!Vkl_AR;u^7PfxPKywY_LiYGOsOO-O4@XAx}!xQeW)obWsx_C@z zFzlBsDb)Y;H{mvo-MU`mH>ah^vJ=pIK&2O_1GD@fM#_m)X7_ml1fa^m0mv3_9zj|MX;$mdGW0-&E&lmC1i^O7)lqGAPqbK&1a~V04+M8D5ZpC5#hv05inO>x zDee*=K#-y>UZA*pfdT~z6t`k6m6z}L-a9vIB{zTFHRqhUXJ+=^^Gfv}MkJ98VY@fk zkQjSKnl8lkiljDTro3ShS|Rlx;QoJ+>T~7HDVd>z64#`(kHF@2$hbjvYsPSGM4d zhsCymQLBk~U;VmRVco!~<@Kt%Vrgfm1; z1I~FapN(HWqf&gnX)cb)P-QAtO1Ar9*J_?2m`PXyu>TV~WD~dBL^J>J`q6#Rx8{cd zTu{DVPn`Sg@I~^MO^Mc*HBH*&)}i;wV!axAp9Ec2mj7W~c<5d|dTzbY7IYy7%$UrT z0l+3*VDzM@32sU9hW0 zn~OC`MzueRIli%fNgPL3A0(nV&7z1=P;%f*G*5Q!91I{#Xgi&40t5epI`OjLm+X=e zOsKFJax<0yTXS*yBNW91A-|0sY78D?NslY8{&U>Bln%!P(Z||P^jEKuMyTjpp6J`G z`(@)qqDAIyuNJ*5lCU-MB8tI~*GU1%vXMiagZ-`Y84G`p#!QZDx>h>PUhyfrHS@gC zW7hfsVn=#{$b=r$gwIBu14;;ZY?*(?Y6lD*66#r()a{#Rj-L?Ltk6G@{MBUTAFp5= z-x0ZR`G;``iI=Ki{s~aR*2rjN@fM4B$)bp^u1#yTkjV8(K&0 z_#a$6?~BjU%(YfijQ|srZ7*DA4XPFkisaQv?={i&EObfK4^nad2A%09S)v_)jNM>S zn;y5^swB=FCje(mj*I8z7R7%j>KBP2zv)+Wwr9E>G(n{37%@OXIA!!vEkrsxiW0-3 zfdn9twMWq&mN0zzYmqUzf%dswxwqz&JSYMJv<3;C%gu3nC0Oyn`h2T=c{m5GgY`{a zf1r@EoIh6m-6BR4cmuzyQAFXn)Tl&Lk!zvbw1CmPX1BmXW*|j_mxvKhf0G}xjw-Xn zR|&d%qQ$+E^czA*i+%zo9T9W6$pI> zWAshQGjtw*X=!L^sK0`sWItVqq4BahperOpjBP1{s}`7EowZwno77%UxFy3=vYy4m zT%b_l?&zwVntey9w$wBCm>FgyYlLS!ONs{0qyz%xdmw-J29|M*lk7{4HA1#m8I5_J z-y4iASk&LZd}P%9=AYy^f2nA)bVlvahrME(-)_h9GG!pi*kz9SE@fFjm`+83Cs+~0 zg~RvSFA}<^$x@camZGMUySsA z637Ou-~0~)Y#>z%QH;rnv0VI$&(h?{TuD${Tbo$IBaePc<5w)`RqmD~+FwxEhW6Dr zx2);6hD<1>WgKxDF7-*A)5+nQV6YlH!@R_Rm|y^Ux(#CKQ~q--6nJQW5PIrel3X@E zyv^Oj)yjEfsx)FkxrDnL-PV$<82#OS^PiC+b@7cv`2>c?Y+i~_HB(R)q!Liq`>khS82g3lNQ{YGqf`uS|hO6equ1X4&T%%)d0&x!0HMm;ek z)@gcrXiv-oXH7>FCIWdI`s}NMgpIyPrq~!;<;`ihJM2pU#fNm^ommlYI%h>Y^)A#b2A#oBr6e1 zSk`sQ>)g@NFj_{r;Kt@{l&S3|^mp`5+;yb9?VB}h{&Tq;Dh4XpBmf0GvaZK{872b? z*eM&!ESsO1`ojRV1Crpl$-nhpMel@u3p3&qbEM^kS2i_}YTyc7- zcfic&@XdV?Mc-Ks>;NQ`GzHw_$FRyPL*`nId#>B$&eD+~$qCQ_7HGEYg%C z0LZULgr4)gpEx|DR>U$%0C#PQx?53bWjf*>##<*e=V+(be0M6EtEmj;6fCbw5$D8+ z8%X~PnC<+CQKEoUp5(&Ydt=xgCpL2K+i87hQe0BEll@IX0WP!b+wS|pX+2Vzm0HbR zA3`Zl8=9+(Gxt3F{aU|iuqk;L_jR>0e@cs!i5(;{n!=ivoo|jEhx1h(o2c#{MYI<( z<%@7&oPCiZpL8vn0`XnODT+=Uw-blAh$0H@f*-2>cf6!(4nZ6{{&&z^r7)tP2%`ui z#Z(?O%VMVim7=!S9Gt>)LqIw?$_OlC>GJSM5wU)AQJv66*qA8(rcZYEc&8D4L@+=k zY+&gxZXx!|EI_}`$7z&gg_ zbd0kQ)G;z#qw_y85~;T;ms~=zVV1oH{O;g(SKtJtogi|x%v}L^ihEcsXoV#90kY8J6|0t z?WW`IELobIFPtizh9*BF2PidPJkvlsYZkxk>`t$jb^Qw$IJBDEYyJTmmN}fc>A)F% zd;o1G$D$@q9ZvrV<-!s+fq4n6d09|Qv=FlTWmDWs{fUCEt?t`N?EyV%4&AFzaYdiV z`*l(~i0q+}gMFueR+=x%_>hz83S=W@*E1O=5coNmzGOXFa5btUf^!CD5=C4WZ`fvK z6aNz9O9q$Ifhj|;X$TENxc&yH>|r`HR%t_RDbhg$U$ET_qZMYne=cG8Ni0!Uh&=hE zKz6wTI18gUyx?HtDH&_@ga@?7t*)Z-X$g$f2!iyhvog*Tluu-9zAkY}VUPVKT1`lg z4c5@L>(+c5BeqXK7g`=5Ocm7W>&YT1%JO&A>$r&OXt_G%Jjaxr z;?_`-yYfXUkHU6`nD%DP@S;)z2zp2U?%_G*UU~&NnGZq^mQpie@9s_g8zwRM zyXer77Cg$MguvAH>GqnfekTqx($?TJPv|CU-1!y~cUZ*i=UEic<^Na5syZ_v=3%ki zn^b4AFk@OW@VsD+_lz>@v?Ft#U={n+dYPgMEqPZT@;tLz+=XSTzB#>n6e!>@K4I8aZQo_>mM6RZglMqA$G1ANNerSq3xJaR6X=+U8klD7Mg1()x(Cxq ziFF%tF>dOt!@px=|wolRX>BxZYx&&KJap{{j6XQsrIfPG(Jm^tiDip z1EU@aUueReM9W*;3w#bsc7o)}%HzjI{XCLVzZJM75eI|Ij@5XxNtj$a<+>%|TkxW` zRcs6;;Wk%l%8fBm@MWAwdA6u~a%_|ofo`YXR0%L}u|74k$hf6D7NyoM%Ssur;KXlG z+MveL_K>J)Zgo)~Fw@;O-hjn(%2W%`MzC#K5h?P|#e;1s`#|-!JZH z$36YhU1qLT`Pn;x9iOwQLDJB17D38c95x&P8V8NTLG;rC`3UA&8seg_$d+6`;ZwM= z!Me@rC_e*}SLBF(SvVEv2>EUU*qhVW;Ia9S_Czyj;fKdcq_%uiM-x>hoKa*B#eWv#9|I5 zLGefYVihu*E$Eejqv-{|xQYx7DTdAzvxta!p7I&{>fYr7DB}jkf7|D( zA)=@=-qgf;1ca=SCsQQqve1a1@CewCk*SUqlVZbPkWy^Cf^#|`UUJVW-#~&{7b~bw zK`|>wj!SdJV*Uw!zIMx!nvWf|66dWG`IWv|&Q%T@sZW?*)N+b*kR~G>$GX|5Eogr6 zONE=#-G2;j#B6G=Wc~hZ{4@RsVj7iMuFHW2DV7HH(R~uB#`lR+F%ByEi^NOgZ^VlBX23 z(|rl$rvH@vwBTLOTvT*D3=t?fE&Dw9P_weOXq008K4zn~w(=_v-jTM@;TI{rj4Pfb ziPcReJ??i)l=Q)i&n|7){K}3Vh}I*$EKeRVw8y%-;r-nUXoVd2YCz4ZOF~33c1Z*H z)`pOVL377_$9mopRamn|ZFuv^jQN!cjFrakjTE@y6i|ko7Ae}!r)b0qP7$lp0PDqR zZH-M17aYyv4|B|Inw8xj94FZOhWG~?WDKEBg?9T3R4mec`mw|n5LRz4I`&%=(o)`3M%jv@?f?)mpbUcq6#C5 z|0RbNrP;IY-FBwBZ8r1E+YDuqQP{W{sT-}SJV?DPL7#Vc75LLpOp5P?ZybVzl$(ZTdizoqxtdx2dW z;8&d@8j~7O3_lL-%~wb1YPhisYYw3^?>Cr1IO*j1arBNKQBj6eql9X>g^?1*M8P+o zC9Z@%OgD?FC{raU=-DPdL~~en|QZn0RjUneEa;_~ZDyxEobkU`P> z27jOFq4tb$`w8m~C^l%=wGEPV-2qzyvjr}yiz{pL-3!ElpoPbAim0T*10UlrQRZOi z7sl_b0RTw^QDebDotU7GJwhSl=Km6e!P&R{_k1;|hBws%WqQ-?Dbf z<Ce%X!3M!;7p#10xm z-HrQ+fhfmEQSkyTNhKM`BLr^kWXqZM^N*i0s$&gE<5EcB=hMso%|aVjVf?bT@G#lc zUnU>FAk)4yW2lys3t|NOTdo1=>U(+tp6f@}th(`K59W*YKt^xYLk6EI>_UXU#aUcteK+uHbo+-a2Ht9Om)f?L4RM z^y#IQR{U1qU>hWA&+4A@95}};c1dW0Ff9UOVH3GybE0nW=3VZ}4?b8|Wefp6y3%I-y5 zaFD6`ut^?aB4CqL9>|2*M*J$j_UqC})$a`%a>o+hOjz|D?nkZBa&O~_57I0Q$)iF} zNuVyES`QDZ;K}I^FAg4mDY1ok5u^$(Glr)@1ck<4J_9d3g*rP9GdJ}T@B1d@4zhXb z(Titz7!ApK2m6EF*KJlD7VSPb`ma37HpWz@5IfG0IBe2hhg_ao-IFpeO{Bof@mF9Ly9(OIAz42D$G2~%%sm54sSQ4j*hU#6{MfW#E=(rpy4H;9afW%p7%RF%?nERu8|hW{%45oy6jrhyes2Z49%Tk{cyjd;(e*XACgE#-|{D9p*>g=jlc=A0EBvK zl7Ic~a+Wpb`FmQ%T{xU5tl7T)L8Ew6Q%X$iq4J&G6oD0Hn1&UmYl9WlH-h)({;XP! z#j=$+E2H@n;_1A3W7AQNtCMC|lll=6`_Jpiyx3}14)aLE72LE*+u8s*Dj}?q^}f1W zv94IGFc@q5$xGesXe?fia%LBtcZp1>RiDp@KbIs0y`XUFG@FlGAJQd*W9%AAWWwHY zuNgcyTPuk@=?0_eI1fPrUrCf8YmQ?D?XAGPuAgtIiM4|XDmjrzWO-|}gC;>)x#JQL z@ci@KaqW`r!CAiVgfB6LV6F))%Iw`#9vh z0D*0&`!ZTT{)Xws>krKh9+f)BOpJk~o82zb8>y90WBL%tM4akZK^!#S5l5+h>4*{Z z-nB!s!z6rzpE~F0;=MVMtT!I_vI-nCsw(m->+j^Q&?MjfCw6iE6Z*6e6CKc}-QaF3 zDimcFch;runXAy+#YNHzz2{!-F3~Cs6(dCN)5S z%trmdAtEqofTog>(@o&^)TfijG_(+gOzTn|5Ehrwz~8Qlk!zwVz5|$=am#J-7VZUr6V__sXS2$8cHQ>PNLgN}z|}S|F;! zL`GB@e8yNz*0BU$bU@fKF6wcyUer1toFI#HOxKD@O-$CmU|~Duo}Ti%{Z0TP7!_{? zUcT@TYO6tQlMkH%9nrnm;^K#{T(GK7--`-PwAjQhb$7|zGtK)b;_1WYeW0dd7Nkag z;Gb2NHOZODc^BkVN2-qVk}MYBXa&f`H5H10y8&sA;%&gzoMeL^YPP$nnnxSOV2$tm zeCx%!omk2*(`dw$6lrqvuPj2I1?<2a zXTEd#3-N|67O37vy-8#czh#f^j#LVLwbXlnhSX9WV5o63m2nZv=_gXk#tlCrFK<0P z(w={B>EOe@avjkdGV)<~&wEEu_PAxs{#JJX@+G0UYfea6CfrGGjb)l--YMcy&e!qh`E=o=9|opoAHSD z#yOlaxu;vf+EIqM*7e$zf2*O+fz3kN4^_H9NZBY z|8+STIU}Xz@Q6vb%INco79mxBR2oqWrYlXx)B62qfgW&1!bvS0-#1`n?mk-dsA{4l zhl2eYb1yaEGrj7TlC%DsvtKq6u?~69JJ-VFhG{4raH<)S%_&|qvU7xI=E3-gV4qZ~ zKRPtA>O_f8ojXya3NtqsI+mjx=Sxik=#Gm!S~2Aj3|FIJIlbJ6XIk{7mSZ~W5Y=U4)MC#LEDEWz7oOH1s z(oq%AO!N^=?oK>Z5-J9%STcq@s3G%7!<7`JIg<>$G>2l@F0#1PWcc4PFiU00nf zqcw`&-0lOvdA0O$-aY)&LqYby2bJJVrHP)3@ex9bEea=c5A}UQ0!&%Nv({&UP_xs*RsopFqWX zE>NH*`?EN*oTig`y-9OteB$(0m0LDx>T5uDUj2Z#z+mx|nD(bK!=cBVTIP8f2o5WC zp@Px|CXI}W7ni5Vi}&>bb{{%au|92I*VeI-ttgDE&-IU*!@m71t&;$kzq@NXS*Ar+F=!tLMNnnXxHEA8$sf zG@#sM?c^@8=4jy7yBKeLLAm#BnY#M?^fT9^O6990p&B0@fBdQB63sB-_@-lEO~6aI z`tw#QLjjj;nPR^mfg+u~!|EYf*CizE{>4!zJyv`+7{iZEjW=w0tQ!))xaiE^8C$~| zY`8B&w(gw1PMTJMEQt!zBZyg;p9kNc>HLvbLWhFEaD?>{C>0x@0ozu&*Ey>=0%jU! zifKB8xHYL{N}8uUGm3fs>OGg!+jQ&o^kR`bWv^Tz3^BysyVaXHlZD;|!^w(IokO=C zh`u0h~ zyQc#8baAb|@_QXye}TX_yaBdnBqmvk^oVH@O1H8M`!m|wxo{3&*hPnNXT5zdNR@UbaPWetRb0S@7K2f^l%gxM7iWoDB0f3&K z&;^9BXO(vUWtb|mAcohRpoiruZnz{7zcZyUTr{hR!iSd1~Pa)R`*pbSJc~c zOqwS0f~D1w1Q{3wR!V?rXik+=NOvxFxas);bSLQ+)BMcj(?1METl25_n^=oJ--8dm zMQnePzrX1(V~l3Pq7#8OmGKlkzwnK9_a6KYTowhGv10I~@#`43C~lxE12^z+JY!AB zYXm$lkiah;=6xsu=M}Ysg^U%o;93TMP;^w7?!;X>oX~0BRE^WrFGHjbuHhsuIF|n? zUVM&gAN=MIls#LN^jLp3!o<5VZtR>tLnt&6U<-7AVpFFF4^f-B z`r%53dzb`&i@L($h>#>QehBD_GRrqsQdFGZbb9Lp@S*vXh1R;jwJqCpa{|#8^2^Qv zds%LjXZAC+BK?Yf87t*tn)=+@5K|p(G?-l&14MU7)<&1WZk^@5ewz-uOi)qmMY|^R zGV90e`ZnN>jnjMb1gJ7b3qhqBa?d<*buC_W|s;%V^z?R7JvwTacA`(i+0el!Qth|JrD!t5^n&&bCDBwPE`b7x{ zzysYau?Nk;-ON%-ee_or%RoC}7y)X+m zVnqPXv35f3+PpD~t!gcPSLT2HTt?8m??I1&oo=v}NW7DG|8;i)Vhs9&4U2rp$I;n>|~C8rW?VrnaH1WP&yD%egVSkVc*4Zi*l>=`YP3rwF60Yn8 zK9xjPgL?hQ9GYL&&q>Tb%YGDD@~t8*{2+w%TICYO8%v`zaiuJMLn@J&;rc*wgr=>5>~ zLqMu!N=Vhmi!QLMK|n6o){B5$ES^QNXC+$~m`1|&xPgA0`Bi`M(NLWKJ|a|`XFH~; z7WVCHnegHuOvmg|TaZZBExi&$QCL(hvd;0xzN6<7ie|{d-Aj!Vg+e8_=|$E9Y3f@o~A1!*lxbr8&~}2fkR!wX3O* zDT7ud$p5Kf-U)eJC@YZt+2P(&nawn@ZMC^G^|XCUvXryx#_|PfYL7P}NEBnPLMBHa z9fQ30;k%W|QR{XpbsG3^_X_7r`nY>q5IH|~2v(%O89fH>cWA3q6r-<<|{1%o+E+t|XZ~e0i7ZZ_E#$ zwM=aH(N>&!L1SsMn7Ko{AcqQDeg-S^-gGrAUdPE{n$P91)t^u`lWAX->!5qmWbffv z^I*xal7?x{&O6~Jtw0CG50jdgkZDe$hvN6cK~BC@Lv84ZZHS}PF87TMVLt50KWr2?m|jN_Ng$NMWlZk%+P0$%MiRd)x$2 z-3&R#~omK$^RQ&6^z z7iLk>6|3%b`p4RB767P^CSRb$)+y24sO z!H9LAq7~_33f+Y;s-uxfh5h^(R&_`NjZb%_HLFbBzDk0hdw6x)VgLVL;>nHW2XtTkJU!nfXtpe=hA~pDepMMbkB=v%STj*{n}5oM z-X{K*2xBj`z)DfIfT10a%rOg`ei{B~;N!u$KZOqyDMZ+*cyZC_J`WrnB zx|1^8tBSQXk9666Re~MnQ(+m>pM|JpL*BU{eP0y5-^5eO`|$#`(2I_jHLFFad(-Ry z2KJtlj5}|hLKas$&wQ@%#laMn{Fu}WA0(pMu${I;ggOUATb+~9jSslvbc|VXrVb`n zr+9AI9!!VimxX^f8r5!?eMrwh%W1pIlqDb~fXgbhCoDnHgPd=cYZ9GkekCpJHG=AI z$m*8_%!Q=z3VJlo(vuOF$p%$La%v|1mbFIb38^utpOySV^|18S5iN>u$s%7IojGcF zX9?`Rl?x#!WFzJjPR| zNR`_q*1`VsisFK z2(_%$dpp9Tn)`8B^^QKj$R)dH;IL2zjjA|Dd$~P7{Ylas+z2yi=x1*Wflp3(r=NB5 zi{Rhw+t>p-v?Y7PA)7>hL>pMj4i*gpL)PTr3DB2npiz_9&DiR$@o_M>{mZ5f-I!x> z@{>(@>DMZ*UbmiuN7E%kmsumzS?T;mJkRsxIij*Jn(TINMR=A_`B@vw~Y0f z#hKD@%~4*Hcy*+cm9^PlSK084z{KB6BCdbQ^E07Vz_w66?DTZG0cnZ4A@Lxs4!_{s zZleUr%+{ib)gdO-L-36a;|*Ir9cKrA0`LwZBr5*U`0tXGB`eg)+bs&;Gf54Yez?M zKRH}5;o_`EH+Q8O=f6(fvH$)(r$kILSj3+9#(`4wDlUKPR#RH`SGKzRnA@$s z$bOvAKa3>obnLB*P4v|AlfF|JT7iMGIHLJ-?D19F#=1x+>-zWqvV@GMsvInbtgHR3 z4yDk`n7jx|j`6&RntR1^?|I!J9;D&}iI^<1mXKnzPVtI1ETom%BQy{UlI|WtC3S1) zU(@QQswSb>Ze;mJ*zY9v)44>ARTsxLz=sU%jZM8tNhMWpH)x9%^I|4KYAu*cxURb< zDTjMf;>N^sZG@YC}Y8*!qL^tA8n1`Tv7z(1xF%TdT7GX(BBkhp*++sNZT1{skzMjpN|xD+`O zRB~{TleEMM6S5Y8_{|ZTqf*f)A4pZswv05{y)#giuikKHV5~Y}OcOs7tbJvURJn2Tx`J;u%0*I)Q5^Eurym<{G*=5zaXTdN{()x?g zRG-x12dk$Kzxjnbj0HiHY!>Csp1k~UVJz%P%Wcwwdn;rc1As6HSL^K@>&bgQDr=(w zA0OghNiw_cj<|#0rs*W=U*(Gel@znlJ=UCPo|f1=;crOjOhb3ItMk>8V^|CNKKFq0 z?0V%Wn^YiljZ9&d#y!mv3fpZ{_9?Tf-5un}FuEcZcL>8vYQw27u0*CED^Gov>v{;Z zY+SV0zAXMCm(knVr*^16Z(XqoKJ9W_su`2zE;9p+Xv$_!un?C!h7UdgymkG8V1L|| zEZQ~s`(7W4)If2?#KdrCe-cUvF*Y21zmuq{R+VM2tPB_ zb`~8)`5+?tlwhP`fwXl{0cdWvJwgSKU+#l_a-sY|Y>lZqRK4}IJjHmUsv%@Rw^c*t zvkQ1%+2*%56gT?1X%(-Iiq|GvheD1P3p@81Gpk|Vd;`cdMm!2M>sNW;@w&u{VQB;| zC3joMz4~j;V;dA5C1N|xHim!<&vQucFoYI4*qLNOTQBwcCQ1fu;J%Mq&`ZEhS_d)5pXM@X&yc{bM&uk>4n(N@{& zAI2cZcgy;PsO@OlL?kDA1yAARIFq*?eS~{`-hDl@&O^)X927G1%+7}-02+EH9~mCQ zf`$}z>LlT*eyTO!(Cc&vWbAmx_@hgrn(x9)9fv7LF|TrZF1}?u=JRLKhbD{^Tc5+2 zJoCUl7_exNGYO2(_>ITz+5H}yWl@#zdpQoiERw8P8IAYH%6DC}m2L(b@+_%aDu%nF zkAqt`{(kjUoECIHEX$-(6Ix0MBhRRaJh02t@q#HW52%D#GDS(S%KfqP?SW&wl>Wb` zjsIcT>W4=v-T^>5pI}(5WW4z)$VXct}%rtD5fBIa}|tvd0X1v zzn)YTK&BbMFga<2NG8~gy z@R6xVjwQIxQ;krqHT{RhPV(cBLJwJ_5s}VD(V10kZ9!0mD5VB3(tYUU>vM%GUzO5Q zks?iG8t+Vk+yotK5a#m+Vw5>5BFl>iAT z7dBvOmy~pIf$+@(&m?)hnD^fgh`Xg+GrvWN(>W%-_m8V`LL|IyW3_6a1qm(Af${N$ zl0LzM`Q|ASRm~jVU-gv=fAnY;I5^A5D9?nQ$i?lbB+Ut%jm=J|1~b1_dEPUx_I$ZB zi*MGsn^$g*GN4Ju_*YkM?@Y&i@+I!R0C80f-*#Y{DH}0VkL{fPqB1Agj74f*`YsEVC3UR;%ls0FWZi?j|X8gIR{Sppl#78vy{ zy%0@iKxsB`5&PVlY2%(&y75THA{;DaqvVgC`(HTDG#LCzjN`3J5@-g}exm0ktw#zK zD;l(BQs6fO1t-l_Je;(nGmY2_UuFw!Qo9OsVxUP*?YL!F_?#IL_$)q=_WuzZ{{Kjf zAos*D82PNGEh2MnFhrQy!!@QY`kWnAyTRU&{y}0v3KatMXfJvu-f8MkIrZchR~86M2ALnxF-%#_D;gCMM<)-3?Ci}QI7Sgsl07bzX^E>{itaDpDaBIORxgTu}6L0;ZB>yj^`q?Psw}xhTo7L zOLTG1-wI!LLP(n<#j3C5FLU?cw3NjU)t z{5cD*4A)5S*{FVWXnl4L6d@yQmNU5P>}OWDUQ8Ekx|tvaSdg~2bbnfYd)ukftgfb- z9cGa(HF74Ez}q}$Qy^k8EAW-=&F{yS?c0I=Ozi1O`=plqR@nA|q!rou7gz2oG4U}R z3Z<7U4;VPBdAn9A=l*Pd?2kVT?E`KNgwBO}D4e&xq3T6+SGiqsBg!J{Sc&ZA*wlNU{T;N$vC%!AcfwBc& zX@Cw!o9LCQJeum#`RXIdC%?yVN8_LEbekTHQvxV=`sD1G`hl?4Ds&AH}l$*df(j|Rw-+;srnk_^b{v#4@3dW_KFYG6 zvg0p5_V&gj)wjvrvs}F4XA=|ZR!WQtkWWv|`6Z%bvaa+Ws;Qegpkdz`D(rz04zos1 z#fu{^e*i_|@zkHQd(CSI&D@h039G-P0sAJ5ZY_70G$ZfbR51;InwQKF!OSW)$oYfQ zMd!bqF~}h6`X;87YRs$IDq!dAc*iW|UN+{2r)#!IUz+gYnW%}sKQ70ID9GKm@(oBc zghA@R1z+Qi0RWuNcz9!V%{1vFW0u=1*(uAfbm`D%d=+rEmqG+8s8%?!t4;#X&`)AoKGT40G`+>A(mZ%4G^9|k}Jzs-2A^}zwt@(=5S z7oF*y=7Hy3WB=U4pbgEoCHbeirw^0j`b;A`E%48Z3_~o9Gnd3k1%0KjRb;pl3SI0v z{(y{N?$}qv3q_^dB_(Kx1ob@*_BgevrtJLc|0v8-BmGv{r)`dJ-AIIc3$lm1#sb^} zb=fEw$2n#$oyYi>gkgNv!ecs&{i!EDd1R^+W6SDn!HwSa`$dcmmVTEk30Zz3$W#lOdK z)>6{8>6hjE(ak_gL{9DJm_wlBeEf*l;sZEKobtzbB`V$zeQ;cZll@c<$<}{0SGwXH z%e*X-R3$I&|Hp&@+?KWTw-JgLu_Y^FVo8=7%iWGuo=5@~uH8CalwG z30_u!iUwI!l%#zMLyj^AkY3u2hsTjjiMD&|T=9iHad4%c*;8md+|+RCw-|p$@9uf;yF{ypQ=XhRKX{ zSFN~l#M~}%E)2jxKg7ODlbHxLhrQcN5fdJrYj+;pzxG210B`TCWy0Zln{}RMh5+aq&&M z_``jw%rJ*#<4ml$37+7_lNQ-aq0u>>(cf(gubrMf~iA+=D*r&mS)9-A-NC zFigJ-s>`O3GHO?t&7dYxh}c6f7Ok9*%&%xbXBuk%Fa!+sMC=o*u4UI6o@h3fWN9UQ zZb1ig_Kl}~8YNe9Y&R4_)TD~wG-a-D#W{9GTga^Q$}iD7A*Q>6urScAbDr$qe^CmBale+*V=xG5wHcfvheyv~gsI%5w zL}R!z$dA?UD#K+X2cV-eP$_;Rutsqr#A9wY=qI*=CU#){qFr+5AdgkrF24j8F(ol$ zlt03v`ysF9(BJWAK<2jpem=oUUVEm%nB~(xpE<5NTBD|4eg%~MDrkWD15Jd_-z^lq z9!ixnvT2@q5v%QqvUPUBDnVDUmfABOm4Crze=5q#_()NV1wxbvNnLTCPhEW$GH%bq zS>+2rKQ>|X)0nh0{FI#^oikIGJufOFlEHnpd*dPO#~Kqw6Wlm}(4gn*%{`PAQN5st3$<>rni=}B$^mKBlT! z9CYZ&J^*{v5OuFb)G_{B28amwe?VUS-cXJ(Lw< zde%-Swv5?46_Vh%Jh?JyJi$I`3!Y1yd6TIst|)}EQ%cCsC%J?LQ$bY9iaFm5%r_2U zu|u@jQ4{H129mA+0Ae36SSD-sRMpc{Qpoc7>Bd-@;c~l4i9jY4%z=lIo%vN(AK5x= zGY~8?#~F;|Mw=0-gRD}ypV*MSleg3!6K07Aw6yYYu||&@uBPR>o$dbsl*zIB2&C1I zoq+!UbQ%B}0GU;QMTrm2q<{Be*lPPTH52*LNQF)tQvW&(R}?_9#W$n3?y)_WlGher(aR)xaR;hcJTDE(k%4bKRp_ zE>~t?Yti? zN~Pz{Qn46b9UXN;2-1nh$l?QVLI9x00merWRvHy>U}Ll#(5A!Zo#TnX z*$A=PNkts8MFG6eVA3#plrZQHD@i$J9a#;YHgY8LDjDiWmm(2!+R)J9)qJcD4Nh$> zn%u_Rk%EZXwsHDbv(mak+^%uZDXJq8%KL_=K9h9ue7u~qh+lptn-p_9%%?`+SZrFJ z>ip#DMT%=lvV58`k8$#9#AUWh3WNUu{L01O%8A4HhzlE=4W0?i5xwus+sLYoR1?o# z2r+;-~>J~HIlMS@}Pti&b}#NY1Y;1z4$MiWTL8PW3US863c{l_Px0_`0u z!JcU9nc*W9A8Ih;JOgz+dQA$hiw&j3=&GlQj612A$EoEvmT4dA;=gJ3E&%g+X>SYG z>j$lq?Sbe13e6*riCW^7K8#Yqfl<-3l}x0)7s%F*ZZ-=6pu%7kbTL)by@1Ii%7X}k z3{MJ#K@;DSSe1%6hhwnHm+JEfYrRH@cAE|o(&=9Z$>8z!s;oMx^wqFF8JKKX@okgS zNF;(+#_`F_aRp=eWrVdp*$>$|!#hi8HKwk&KwELX?bU$WJBtUfGZ*@RWr8oI1D znrY8DB#aWMVEl?WBe&TQ2vktd3Dij&oBhHt)y)wWEO~9c3J`YY7IdCBvu@+ILVlCL z)LKCt!WFm;1y?=n40rr2ous6y={y?-BiURwtL}lI`h*_~*m*gm6}iM}V*4d<{{WJV zUA?0J0C;mYZaZjZ={zlRERnAo7Mr14f>BjBQ7kQTEQRlDa~8CaJXGQ3kl_p-vZ~(F zH(+{jImW|y=8soci1HOG-os@=g1GRjX@ zFsx>ISrslBRQ55pzD3$uxlm;Dl17D(OZl@=#HT->j4*!$DuO0jY)W}O7#gq!eC(;0 z)@9k+eOZP@>hfH$*gq7p0H2SRW=KaRN>R> zBoDR6QP879+iGaOim+lSn8pQJhsnx#h#p6wPo}As8Aeye^4AXn7ULbM!(Ym&muIV1 z>#~@m{3)NaCuM49>on?*wM?dEWb(uyB#eu*aO(d6YnRq#LxoZY*40H%69s{h)>Js< zGe=ahpT-tFhl3?9O8AG_ z^QXfrqm_{fgu4y)@FrJEr%65y3Gfh!Jp6c~LB)m2h9IHF-BxUKzt zMBOBj&i>^YdwE0t%e!GPP+~t}F+XN4fZ<|+ki%08K1}Lv()M@TYJ&vgQpbibVeM8Y zP4-UZ>|MLVU(!eJ{{X+^c@^cfAcATLCZ;APCMGD;4LiaTNcZ^u4txRZSuuIN(_dcD ze=VB1HYkHxpaQ@x#W~0eo_Sa-O0oUT9Yn5wR>b15NTRsI=gxdtWslWAf|ZQ(e1Qrc z$HRrzSX3~V=VwQ$yH7P6>np@w%)%`ke>%d4=hs!ryEhj)X=VO7O6>F+M}*V=08yD? z@C!pjPamW~2K=oJ^cMdBN6*-IpR#srGWHV-@{8~=tT#&P_#r+8`mW3lc#6TXQ<8%a zhFATZow6>>VM4lUXU+@qS&07tk%RvLd6O`n%F$Laj5vjKLGi`VEN;KFRFc+WQ;=tE z*d9YweFtYLXT$2HfSK&`62#~H`udqYWhGCHVYq%7NcW0ZAYhPnl0`f=GeabKxo2q? zL=`SE*D4gy0}`E_q=RPTuv9)WW_oOFbKc zYM0VA20epAh|)F9id)z}n6}6w6u8U`43+QQO|ZYR^>K8Lr5rD{_?0(J@q0ZEs9{~M z!>Z1V5`g~zLW70DxL@IlKj-WtuvlO4Eyr$Ve@WwG(T1uiTg{ISAJCya3=nUPV_E!#`mP)}= zC3t$mF}!MgO0Ig4`MI%+vvf1XJWx($iNV3fZ^nhD&ME`-1rOX5UMfC>CZK`{Aew?| z2q3c3JgwZ1Z-=^5k07M`RHsJ9=N}+Ss~BX!(#`!?S7>YTN4}Q}n6Y-6UT{GVtjiSV zJ$)+_C-d=l`Z6VfGxMjcnP50*BASuf^UonG42$KVg;O)+r-{Cd2o7KB8T$iY<&^Bt z*cvY);j;Hy9t;1?yhwpn7kV@Gid1Kmc9MYu6`!pX_tyM9}BG1@q zPuVjzSK~9&Q-LK!tZ0fRG<02}FNS${5Lkm}y%X3EIAo7y8N(*VCxl5kkjEN>7wqM2 zEb)hTPAolI;4TPOBrl4^pz&iL@7ou{r}2b^WxwpM883}HRVEcfC0%^NC>hL^Svh<- z93q-7lRsCiTOO|ClBTwI^wrA|z#yZ3B*7c%np`95%Oed`QjTnRaQ=mFsbZJ!V0ZTm z_OE0gI|b*WJJGY-{{ZwhUb4Vj1NM6_WU#n=DcGMw*lNQOv6P6mQ&VB5 znueky$?>_T`h}8t8TPn^lOL~xq_87X*;s1&(#^7(8aZ%E$%IeOo&}}V9aIot^pc65 z$urHd^u3aN@8wl7a8I5HamrR}5Xm|qp+ki-lNY6Y?}S;B#-;64@c|Cvk`IxJd{YsI zj+KS-%J9$vW*dZ3(8tOMgB7tXHmmQ5u@lLAUt;jNd>{-z(@m0M{gcFKU}kxxi@P3S zcAj2v!56H{6YR9rrn-0^9O1e~y|=R%l{eo`^<|T^)l|JFfp%*Vn9V*I!1LB3Si3uh zOeLkmV$adiUFzSZoJtHts?pS{z^cNk-vI7QJRqSQMa4%2RzmdJxC7hx04F+);jJXu zV%1eLdp;XgC!|GoqJCIY+5BV$+DU$KOAoB7y#9VG0~H)v)Z&uK=*56D=VbCO&B4nJ zVf7@e9uLaHDM$VRQ^omEM9<-1AFXSCX94O~9}ni&Q?u4ku&LjYEJ~H`@DhXwxcvp59vG5z&kZlT0cAFhxm$? zuBG$N^CXfRxz10;FsQ4sI>*y*wT>n!`zv0^p8fn23&kpv0_`m{;r4~(GEw5vfdGv` zQr2Vj@{s4f^Su86$^rHD)4)XR%E-&qHF$jRR5Us%BO$<{9aOAu%GZz=e~Llm*RAhh z@h>I=vI9$7iII83#7x<~>=q~F!0nm)BRpSaNm=51KIV{&Rnx|KvPTc>R!-H=7vD|p z(?$7}5i`}`Y`zr|E7`N!yDNpy_tP;lXk>~(nUXZM!^9N)8?-f)59JhxFPnNP zDewy1FPv3RWBm(O_O2-Y_ke^nP((R0Hn;l(`p&{v2>Hcnc2Lq`Z^AJt*#Tqo!d|eP zHw>C83Z_+7$if&R)G-%4x!lRQLYKFco)ZT~pv180=&GBV8Q56ht9v(!J_?o-2{(pr zj|t2-X&|gj!@4!~89v?VvUA<3xhoOJr)1SpK9+;5kLLdXMGX}kRJ6!5*QU7BU-7-XqD=ac5yhhm~J ziBZK;A~#@=Udsrl{ERML@Ip-A4Z}uzMJ9mb^ zquyiw{{X0a^>YgGVL<%_545Wr%5y->%zCJQ%L~u+Rfqhd zJxZxO%NzYu{;7xb3O=&9s&UUbRL%bYC?8UxZzz~Qq63fv{SdYZNYI875Aq7`@I$&9M+`6r>;TRbO3QDKzb9``*7D_~=}I!1&2 z(;}sbqRneq094cVRtJfqGebJc;6nD!&0*C&UrxnG874YN8cDe#Ft98N?4GHwn17QF zrB>o_NlAskDIk7@{NAo^t=ek1zW5gVvG}7rY2C8;P9+b7qh|jAvN)`MG|$<5I~9Y% zMje}u)nbu-wNG1~-qQa7y^m+PRR`?J;GR}6=V4O&L~=eyxV6CQz?lOedjKGtScQ&J*Fjb>8d#-;;py21 zdly?G_{bg7N7p*QC4u8GlIY0mgfPR9% z7wt7|bQN$z1oCH6QCGoMaeZNUevy8V4rM^ko@m?Z@UZCqddP>PmK4waP5l~FkNIhw zr+@hYsppxAsxPi_{TiqT&m@Ima2tr8Pn(q(ZM~hOd8|E-%*q;Y?;Y4pV6z$<=q>(- zkJ9$D5~8NW=7tDm%D}~mVqLD7S65Fs6wcA_{2t;@;VRYDAIx|CtUDOQtHvit$;43t z!1aI_jAsa>x9})CIgG|=w4|vSn2c3XNYi51f(qWzK_^$n*MD%S=qRPDtdvCrLhgB0 zPnq#xa0sI5G^@1o^TlOY^$hG4l8VSWhpJ3-3WmOk*NOiC-KVH1j>fafrKpzz;<29D zS3?ywJhKQQ42~9R7y`twX+(IP9F*nUvneU(WYIXa>~M2P=t9N&e_uW=SvZ0ovZ?zm z1XUuWRMR}=MXm&BLN&dW!zvzbUjtFA?SZEHDI*L};=Qz{WlJ-~05cKjhc1ddFSfPt zKr%$)Bw+PM4%ZkuPYE9O^8>DDj1T_+rDwQ#tKqnuJ`y!+g!(-exHxD+#_Kc_)=TM_ ztbVta9a}G~Wp<)|SWK+*daJ{GCqYGC%#*^*t_r3q*WkEZ#vsinlq%eF6ONLNhFRq* zf`qbsVq;k_*LBH>l%2E0;E92Z6KK$~cIF}X#Pz+wt7H41c>e%`ts{qlR-YAz$){%f z39O6ng~R?8p5fqCu`Es>7@Br&dwYuc${x2Jxx!!LIB&T8U$QcoMdqbD=BpK1fZ<|- zdQnvrehbene)^{Jzf?Z&UKNjF`wrbdNojwm6E+h^imV*^*7N?7**$%>8lcd z{{SZ5fGYR=)sp`Jbu0dPq<5>8`J18soBL-Uti+|oH$r(cp~My)s_LZ~DVKb}}x);ki1nm7lPac}mTh`vryhMvMD*ivIvPJF^(UuVeypVl2q9 zL-`aiF;#Yl6E)Sj1jV7e1E5AO-Qj+6``PH{Z`~V*{1uX_X81gh`CaxZ&n7G3cx?Xw zWTY-2p_2f^<&HKC^z1qhB?cLXOHV+W_iA!C^w<1h|Ro3wa~ z->Z#7{{ZbX{{X0~zWv7r$mZ?VB#Z&G8mntWv5!vS$zMO3B*!V*BZxvnOvRKYT25+;)o&o9wA!{{WSJpo<+shQ4>! zV3Pfja^59Z=D&GnEh0zP=0D*cjk7i$%HeR;G66f~lOSnW)n!~zK|@Os zmEo$p1MGwAXygp41%Y;=et5L3PnQ9pl2@0=_!#Fn^(1~C3xq!niNE zH!Lb2SF8etW_u+S6jKAikT*8KIh!WC<#0CeJ8s7hCA!8dS>ZGP0PR~JuV;)1*Y+xM z&0kDJ+aRy()Kn781yv^pt5q#GW?zOyFf~RO#&vL=OVA66x9{%AlApeVLZqs3J%iY z^-j=7J4sD@1DbBv3xcN|ro>{32_I?S$%OS)^qsPbX{e`UCMS&LgblcreluBAE2*K1 zX`J>cJ2#E95`HEpECgf`?7?yDL{{Q(K3RhUK5_!=%_#;fA{)2;9ya{aGNETkXR%MptEm>^>9Cn)M-i_$gPdAZ{AtYUx8#HyX^ zDOE4|=at^CB}w;{C)`xOc~brAZ@EkNDSTAps-MM8;->LaDO8F@B9T2gIXO8y$MiV0 z2W%#S3i_C!d~ACd9;#*SxD_+YE_ffa+IwpFx2lJbpn(KVfR#{&1GPn=jZrGx!eO3br?0{P+I=+ONi{ zjW!tpo=`Io7ivo{>`e4BgHHDm_8Zluw@PJ};+rbCPW0iJd50eOiHYnR08XCrn02Dx% z2-pecLr3}}Tg|{KNWU_s7W%v_Eu8D2{*JoBWB&l;7kV_TGJ=%OVxCZ_@jYOqlz$lr z#toooRgbC21T6}xRsv;HyPWS%cg-V@sZBh}Ww`{#gw)$0<`fQO1c&`ECObht-vZor z^9}QPX2W0F+)93V=W=vYw2(n-fm{40l5f5iJN_2`0JhJIq!BfXecFlYxPnM6F*zTdON8UpwUHYZNG^o5yT))@j9>OyZ*|g#qmk& zE20v`NClXv>u?InX<&@7!s~F22nx?|@GJZ?S^WP1bNqRW{{TIIjNkl+tM&&L{#MWM z1{}Y@SeKyLe!$`0=3IXe9uvR*)w@|goiW(KJ{w!r;mjzqR=9Hp>wkq(P1ZBRG1w&S%){)7ff@UEhj=iTto?(;{{Vw!`v(VM%mE!JG;uVrqRK3#A zE3W4qNh(|>hFns)nJ20ijh#alYH3^I!WCUR=9X$%V~cn|d)Po<0gCxOp5_sWV)Zrk zRaG~g`O6rvOai=C&@yfK1YPgg2(egXkIy9qGo9jM3Ywq)0B=teU%oJ`%#sHn9gnxbCsNpz(S&mNQlwRPs4F- z!_QPIAPyZubX6h2J9Gg>5Ei>QQ7OpeDDf-5^n+miE%3ZHr|gx?TaMLXlKr(TLX!!r zsVDNGbN>LaDB?Y)q>GuJq<`8JeX+!#Xs~R>drxC9xq9vZUfbI2PJbEP7EBtxv#hd) zD|Si`I+dBg4yy~pFi0h;q>6gUm>&1a6&4$b(Nxhu$OP;;*u`$r;od&b&-+G)vv`$I z(8mMFMb0GiDhe*bVcM~o!Ff~@b|$(3R{@(s)(5LRBQIuXH#Dq72;SZ_p|hpbDwMj38ce$&Sc)F#evsVbeeDUD4Whk4_7Qc zJLjx~f8?-u1>acT4FM|%3}cvPczLiPik2og#Ya4fX_)g*hH@7f-m39xsbH&$8fTGs zRxe305j@f`4vY`Y=v94P5qx#?l06!#>M7+FRWmXF0L&^)^h-2qs;X#}0c{>4LvVbHnsMy6s0as|{7;*6<+85sk{xx$9$%|T|4 zfasq^?9I#_6+W#UZQ!WAhZ1W+iYc3T*nCxv%RFkY8Zgzesf*fZ8iTb$G%SUY&VdU} zfdImWtBdqRkN~+VcwK4Yk~84uk?vdcoBqSd{;b9S)yw{ zy7tc9QFudn7U#EheiAjWK!53=n4icJBQ;dAo^N?__CCW<_{t25-I}BGonERh;~(~| zM+op^f9ZGFPovfQQHFo{ELl4RiutotQwiQ?Wm;V6KUF-cy{{ReC{{TPS{{S9h z-_Bp-Q~Zao@D?xpouA+uA6KjP6BPbd%k~C`)!ya1Rf+!qJ<8p+^?~~gy^?}nkk7Iz z{>D+n&&{S8Bnp?b*g>S9f>!GczhPaYpqzn1XJ<#kyGc2-Fqze4vodUQQ3O3WpnR>S~E+Az#xYCvZlR-uV`~mFh#*o z#rbf?&*5kvhmPa@udXI1#YycByz?q-8wrAfkXk5PZ!&tH(}wdPDmZ^Ot_FI@0>I$b zTvC{NJ$7;*{(gVpVJib?U9M>o%mR4UvLI|_7=u}bE$RBrSu~0giR)|A^ z;>+SwT}0scV}eHN)*Vw`ATk)N_S#IKUo!S-jw} z?6zvR@7W|-qnVA>r_E1KRhFW5Wq2*g_be!Ogwx|PoRY+HbOfo!H0n$amw&Cpozbfp zFh;`0H#q>lJ#sG2V8Wi45GJaoU%gV{cci0v5`&u5-OZNpwreu=w-F? zyYzn5*nCz$2y_1cWoHGS{{T*~Ncz-L!oU2cEA|Hz+5QQ_ ze~=Y60favgs}Fo^aBZ2)lBl0Bj+YFX7f`*#SeVHp`Y(*1`HuepwfmnE{%~KqYd?_x z0QxJucd1|DdcWuUpX1zD`OExle~|V51jYXVk+b{(Me6l_!eYP4Q^}-o$o~LFlBI{R z)Qp*hj|JZ2F)U)PwtQHyPa%FrLZ~}6iHi6uC!TDjyD6)wUrkv9Cx!gj@o-iTO{5rI z8$BUviXmvDWMjdt1N{y*eDX2ArD3psZGAI6Wec;=eE79G?9_a#A1ClcRtnF_g058u z`508c`O2{a`51HzSo{7#OCO|bw3740i$8&YEOLL$#cGeE)nxwwnNvK3_`H?HXWss4 zwDl29x{gxLOpslt)o)(s7B!@s88q%f7n8fRPC(8t{mpdNNH%$%s)llaNQ$_Pe z;GJzk5O$B*X3`qyodNYcl$0_|V`yulHYM_0l01~Pm}GLX)^PxbCpqQDLBs*D=;akQ zF=x~|@(mkqhs{9IEFrSF(T(#6Jp7X#MePt%jrH>eOVFmvK+W8-!B7SC7xhn`Nhlz2 zos4E@Mt}V%X>lifXdPoGXYls9sFR2FWJ({+Wbp?Wjrxk zP?-e`)%29ksH`_E-t%gxlMI4d+d)z|oC&hSs%xNr9aP8GH|NN)dg?l8nHwo&kLHoQ z)pjC7aSFL5Vfr}*gyGcHc$IB7durA{pk0>jL?RgJg)LAv4dhfb5Yf|8(qv;kun==u zr$xoHzE=TpFJtXnGU&+_+JL>&Zc}e$&C+XUJ_({E1SknXDXFA6S?Bay?$j~;A&Jjb zF=AL`P)jsnTbHwvh@i>TFG_W~W zhy!AMlNW5&h;^vHXe}RxZ*>%2*@xg?5sD zc%-BF01FwP^RY^|`pQ)D{%rEU_?}80=uJlXK(fnVPZE)j;kZ2tzZ|Y5>UybzB8Bcg zK{%ZC65rXCn!^|p&L>~=- z1X;zrf~#PLeerBzxnvzKrixEysF>%N=ZKPw7?_F8epejLdqu!KLYE204NM`7Yko(Q zZ%|Yiz7+bG({3w6GZFU%N;mXC$nbBYhcs*ODQLM(Mr=J7XJV}{#9}d4z-_A*FYQxJ zhd{vz(TtH|IMk+WLZRkIXd`ZbD%y&}1r;Q(H`T;hp;k#N*;_D&D>FBB#hG9fczK>l zqJ2vRot)jl-in7FqKVt~WFH7cfjUR%i$_OVTU6jJVxP{*=a?zhDCR$+*1o67tV0o_g0^T` z3#q0vL=4(FfLp-$*o8$6Y|sg5qkcl?=_H_KIr7|)E-i2taY)(eC3~lmO!ueASTf$@ zDf0xXLnSK!tPFctWSz5ot{@S@ZaS)Jm>&cNav@-oJnaCvrv3}sYw9N^`^jAE`k&F? zLcNn8uY>HJir8Fe$_)IU=>iFFV@tvRdv-jp5 zon_o43Xa1+FNyk^Pal0RfqO!mRK;+CP`hH+23^8BXcBZa~v$-dWD12{zex@&qx}h ziMe89SUegmKhT1P7Uq^#Fb=M80b#I<$ypEw&g%znDw6*I;ElylOh}27pH^8y&&rQ^ z<(w&Rr96NC0K1h@CGKK@x9Tpdj9|l46o;OHRIrwqr4o){IaA^=mQ5_8OKEp?Rd`i# zn8f*Mk5F!?u_|WG7$kDDg@{g#<t++$}9_QRiezxw5jdHrURsOp)ckA+_%1t)#t&D z0fXr3shg4pZGW3JQGf}yU!V#2X&wfSuv zh~aqEx1p?S?{_RS0D@R#rj_%~{OsJbGoG<5ZmIRG!puyS`Wdv#>Z+dsWqu&WKQAZ( z?7jf@%p{4Ei)J&wIZ;njUltn+=5w2x76|^Ok7JP3JXW%nI1X8%V9MYY4M|Y;%QTf! zyn_qdsDF`16vFGND5)DwxgA|+u&hR^H^V(mT&xDiGl?1^+%fGlz0t5~8GYJ6qRjHH zti)X9$#E-+Unv#iXg&)>GX=arAevm76LO~NQ$RtkKcaz{1HqC!^Gf1bn^jZnLI?~H zS@W=xFPbOvKB?u0R!Cm(_cS{o{Jf6V$C|H*LwH2)i#?wDy-sH3`xEnSYUt&H0T6B5 zTb-{UWdyJec2Hp0E+bU|IlYuH7kD+Lt^F^vcIy%!d-J0C8o|xP1Ss%YI$@^j7Bx?a z_BMDaAbj(Ou(ga`sh^e&KdbE3i1u_=&5FVzmTLxVXKM{0mbB9{1%uI6MLDjlmGL}3 zW)X^2GNTcohNf0GD@>yH3ksuneI^@A3&$ri5D5POLu&=x;NMeaLl5TG)3er4usD8v zP6dER%Bk{y1Vv#ZTbs|qq#%ec-pxwe3( zw75CEK&LZJ3N~@jD<4!9oUH5Hnm1XEm920zP_dw~#mB2{$we$8N_v9#WxLqnl(9s@ z1KdGV*UaeK7G{k>Dw6`Cj5G(<9(hgIw79;Z!~>zts0s+=&SZFPtNFw!>-2ccBZZ=hozx{Mci-98E59_@{YfaVtX9;w)NJRz?lYI$zQTyh-> zFMwh+bDD~0pOIEkRnuY&ZyhlpvHt*D7ByVVkYPp!NZYZ`SoLKypNQiy(ilhqk?5ml zFNYXI>*jQXRKiC;LY*A7aL~a87qOhdla4AcJh%>L5k8+){VeuFMA%$tt0;c+QFy5D(UE8yU5Z(4Ux^y zW>|{32<#1l4^$rvZU@x|D;uD|7Vs>CZOYK1ZnL=lh_u`vLKY=UtQFI<(dA>1bDfiU zOB$SMRY1r2vE(c!T_%cn78_mJiK3yaY_dw`jGBXQ(%_hdbE_+`*e9LZ7swV7P5BsQ zBz};${SG#K?`wTZpo{0MfPdx|4Gn|qnf(DP294!{X&!u-sPx@IEAQwKrhZ0I5B%g< zrA+LGnueP1Smd>n3K?N!0NI4ZyF)k6iON5K0I^B`05=kI4z6*sZsXW#8IlG#l5=WGbAWF&=)q&y z2qTeCmV0Dzh-z#uN21)lk;5Kl#xk^>98;eubTA0cYr5^xOx|qJ>eH6!)y_s{m-D@? zZ&b=?&$MK5ELhDv_kpvp4SbKP ztE0$qoIUYETWhgl6@&&=9z9!eWYPh~U3n<%zx@>Mzad|b<2)6lAy(OL2D z9}vB+s+`DYV;UX+&K=~0H4)W5pf(pBT1Seff(UAIK1Ro&4Lu6TAgF?!k8`+OdbBTZ z?8q2-0oLze=txYX#9s^KlRV!;hJi}_2)b-ZvAUA&5oO{nSZkj@!cAEr0pZTM0C*d! z=zyi85K?6Lh#vQA=oJ-qBkf+rHFVO#MB}Va9aVk`1|3}XK|zZ=5jQ>ASZ*CtBPwg( zT=8cI19rd2sON$3K}N`8FV5c9@hc{O8Oz`#?;e9~RSYU0D2&?ARn)_y}>UOD8- z=;QJJskuVRjrjtOw`$hSYCmSFr&l)Vm$0}@4?2_!jg7pDCzYZFc^&}Y@n%BBo9s+2hLaB{Au4vCd=5c9;x>aiG|Ci-pQ8rs}8 zLa<3BvTvL6fI@qvWVGW#>sytTR9rLF9syw0ix-AbnB|P<^7Ca~hRF>rWie)BVD@Sp zn_nyEj*P+@e>A&Ct2nR#E*4*{W+XW1w9u_VwZbIaCZ1&6D98ftLGIOn`M9a)V0#}N zZ2Eea6$S@FLt|j8nV(Ls2+1ya8s{QIu1h@?*^`kP=IU2LsqrX_=C`O!Eg)sgn?0Ir zLwYFU%WDKcIUE7ML?>a;=KGYf{#m_~r{aA6peMr~?D4d6D}M!c7cQiY{6EAwg&b2i zNXJ~QH?lL+GNHWLq4Yq3W6W5l13d%ed(Nj{0;8$JpH3n!+?!>QOD!9jD>pA1G%skw zWrBJ4lQ)~0%q{$`_Iii1cC&{3pKf4P&Z+ssO&zsc&Z zy9SqgRWqb#Qz1sH1kB<~qh8U2LqF9S39~vEvzTp(PmZaQgLeg+7ZO_A!D15_Ol{DP zVO;GB$3=3BN2(FT8i{aYE)-Q2Wz%Iew5&3`i-#L@QsP*Qm2&fbjBncBffn1yqa(98 zuA#{0JV=g37Lj)yQ}4|mxjdU)7Y3nI9{jurnezgK_J*8pp=OqDmsTYnVO>+xw|MA` zk`Frs&XGN&j~m$;nY@te;+)vyb1d+62a!>_#%H;%@@>>1rV3{X3F>3Yf4XE7z1WR2 zGv&T;ZkqVm@00aDGUw<1ycNmf=D z1fmVt>l7+n^CZ@I7ibOUjM5C@MMp&~T*IddbzN(Yb9W-rIL+^JgwA7&a*!+lC`4tVzR`?xH05O8v{otYt0#8F}wIm5lZG?MnXH$ceoW?^Go4Sm`AJ$7zdU#JxMIU?r!~iZ400IF61OWsA1pxs70|5X4 z009C65D^3g5+E@XAuvEe6f!e3P$Cs0Btl^S+5iXv0s#R90LjSYaxzNXjz&zGGkh2@ zV8D=!nKRc0FM}TpuI!+CW2Rt=5;4;zNs6W#e3OYz2NMNf6O5a5q-m+NQxQKB^BW=E znCh2YU6>;gEEzLw-ccDFz)I4(vn6Qx=!Iv|jE7b(va+_B-lvILRzmsR<7t^W9FAup z%f1A@JQy%z=u4s^B14FxD?W&*&OY&L4qo(`n<(U3WY5yXbd(5)pNHlK1V4dxR!C?_ zOl-->$c1@8#o$KLvk;icS{`~LCJ8Q#WP2{MRzoz`sp58#k|MhK9nr{s=OaHu7QurC zKCHSSbyjwEZwjRS+}TOCG*d$GDe)e$ME!XkM=~r-oA!QTck~k*yL}^k*`eA5Z7V*K zv^=heg{9U=g_pq+UKoaH#cIAKX<8i@Oqn?xjz&zgJ=HUTkB0^f7%}%BMP<=;amtk( zo`Ft|1x$r9YZk2iq{L7lB7Ps3{LA5DLRL=s6SK4t6=_-Ym7(Qzg{Rja98o3g#ce;8 zYV7lA+AL1_GH2*=Id;I2g9Z$J_t9EC7iDxJ1xr@(AruN?iBWqrN9127FM(N!*@%FK zO3YW4qAswsy5vnhu2>Y)=>mQwgsB*)ky`7Qv6c?Cyy4 zR(%!Fh!Yz~to@`SflQBzgpn0|{Zy}m7IseX6EZt7URH>_bd9Icm1I+!7fOUYrew&b zN~0aHN7(fl>M_Z`mp?K_1rbE4>^TvU10PE^j52y-jy_C{+1Ut>MQ71jSrID8$x)8l za+thS8tR|O?7Jf!U6tZ1J275Xi60k4Z6V#2SqJYK9F9jKQOO!+3>f(GWXY2zexm7B z+6aQSJ~na_5+AXVvJ9lXnk0s=j=3^mNNVgtBhgv*S4Bi|Dj}gFiYbf5Mu+qj)z#4Y zKG7AVG4z$9FBi!?y$4j2$rm^5S`kr6=zWvWJE4Qf8j=to^d?cjh0qZNDN6k-ErC!% zk2E2Kj!0FyN(;RNgn(EO5D-~9C}qEgec$(kC*g2TGI!?AEx$W+=MJA>AJz}J?qm%;p{e|_Q&DVlkq_>t$)|iG zj)Fm5Zlw`+lIaBy88L4z>q4mOW%h03LxUXBtO&<4Aap4~Nb4~&*X;!p8%!Z`0rR#{ zER+HVhOIVKEAC1k2?)K)tLW4h=bW|Flg()tNsJ_R^B&aV7XS+Xo>A2XE|>td@Y3yY zFp$kCG={_hSpx5+3f1AYY`klpQhljK1>0~8nVsm#|ImZvR8p-XfLFSG+h};bD$~Zjhe@` zAYpPH!X_}80J+{nJ8VP}pQSx)ZZt%PtM2f4UyW#QE^r7el1~Zz-^&<$ZqBPmF6!Bs zIH+!d{Vq~J;1klE6Y8J}-^a7SuZV{F_lfH^r9y+=J;OwNb!8qJ)wXt7@)!{Q_cGRF zCXnWebktssLI&AqbwzvV-q&A;-b%Bz>4mh%H?INqq}C~nPwd|_8*ziVWxH7f*46F$ zpN7+%$QbPp>*MIJs!HgiQugQ`272yw>de)pN`#OcXyd zkgC>~4UC;|m~-7YQCydvK!I(WR(AbmUjz@Nxl_5FU9o_?sO>9-l9BhE)(Abr`pT5S zrAKaRE(r_%qh$~jRMOVjIYPQI92@A_Kg}m~Oh$xdXW69SjKf6s$VCzmgjr;p2$l9x1dhMlsOTt16{ zxn>1^rB97_lT)z1eJjF*GC?K4bi-vNih61o7gV6(65qLLE}^{q{Orj+y-Eh;h(LA9 zuaY1S5R94Br^Gp!GwcSF?F6W=`RR*3(l3p*6R!CoT*FH7Dt?go>u2Oe*=X0E-buUA ziX~ecrxsB|M5+^*eeA+|N;mS}Tk^9k;z544rJ~qmic@k7=3N&CafnT*`Kc>2c)`ts zoog~%FxE{l?!1kbpT0_x^|i9qMDK+`pr{Z9?z+=jopfqCH+r_5*IMM`%Xvm^0{h5H zR0b!T9kl*{v%&?~z8kv5!~sOgmL{S|*OpyupXvrbV3LRsOj1+iSu`8(?=0S`gZ%U* zZe&a!h%*u3ejlY#t`9PySFY7tKJ4duJJspwTl4m7Fyh0j0D1PXR6=dnAG;o*IjNe}A)@Z+ake-al^KjnoMS?A+`<2sLP#`h{Fby#GzElap zoCm37`XkN6g4Mhi3x3LT%H+WXsW>-fGJWZhW=`?~-Ur|zj~-a4R$L-z%< z{n@YSXjd6ai4PYL!hg?K2+*rd7!r$Y)F6L&&99QLvs=uRYa4q1nDEm#V4ea`afyX}VD^RkklxeP5YzL2SL%Kd2Y@1iml(CBgY1b1-^`i@OnaD6roTc_^|SM>%o$3#RQRgyYqMH0 z7k8GFy|N{>VhFgI4>u9(@#F}AG-$QwoR~RyxzN<4_oDY_5%JR|PaUW>W2-e(qpp$d z&PBG%{(1+tp%fmx!i%+Lgy<2aP1$;0q&ewlMS!Oh|6auc3a%EUCq0u1GztQ{vL`zO)nLbL@W$N&OK=Vhis zot(txZIjTw;(o4n87_JA2WGFFWWNw^aU`-hUStdE>%k`QDS2eozh}U9mV@y+O44x0 z3%WAhn{%*>Z2oQ#)QOA&9q_4)1_nvsG7(q6PCkxZ9}l0y?ImS*6}Q#=bnHj7 z;xVK!X|_To@b&@H?()3^J&JBP1&&JU`bRhg7+rkzQ;HyEsKA;Fo6ZvIO*4f)LnHz! z8mN&@jg5KeZIx9TM_4A8#JudDl7Nu`z^J@Ih5d*GtkY8n?BY!^ybMHH9D7t6FOFXj zhh8iT(cgFf_l!@O%fn7C>Qa8}d`DJ_6d8Jxa@q+FT^6MIGf7{55e!A*@lN2W-#ub; z7J%bPiFRf@y+*ykDf7VzIdEx=nLbx2?{XK(uBM|f2CWH0?vPigAjKr-6D;z*29*eu zJ&8E$3J9pUxI(7rW!~j>aU}vx#FGk#uy2;wCP|YV$5qXfzJsoc98X3_bpF*ekM2RlR<4w`2AC^gA#a z`a0_u`RvV$x*S_j2T`*Yj`q9(svcqf-a8coyD+qh$~{b!?`B^z9tBaNFGzT&lCfy) zEKgYehj_?*K^`F<4v1~*#SP6kynVh^6p2@f0SyeZE=wok&O(-|TknSL5GG`M%tH(2 z>9l#2lV}{?yT@vq9xpbJx>BpoU_)?C{{s!Q2Lv~XJbO+btk~N#&HO_NASN!3_Y>`R zRQ^pb?$0%&(6N1VpT1=A!OQG{DI&;I4%knQ#<~36wKLYsokThC`i!-7IWGqmxLEN;nwh@p_C0F?QJZ>cNZH$Wc0v0=hhJfv0>0=HQ zE7$Xt`7N8z&fzO7;{vOL!(kp*(=pWnUsK|x^tqkmog($QgMsaFow`kYzb0$~6HutN zscQ*ZEetQ$DeA$Xc$v&vg)iwTTdW38`_wiyUH`RqNd=D@5Y%aS$dMas zT6{j5Aq2MwpS;b?fDl7Y;1cV--V_cXA=KuHAnyyhIH4|iE{B5?3`T>xFNZe4AL*+E z6L!siz&_~su3jn?rX`v6I_OnECfvkbD6T%0ebjSA-jH`rgLt}b_*SibEuKEz%GhLH zFX!#u`c}b3HeNL0^(qGZnCmV4gUhY;P@U(1mWE;%G*il@NhM}!zeD0-8brsZZ|9wu zaOZ`-RLO-z_g&B`byCOj^l>XaWiv%Nu&1Zz@QXR82zCb3UBJQZEQ$(wIq1XGVoc0F(d_Cj(?t6@HvLFW=L4&OLqm1^DW-Q@aVE&hve6*~q;*XE%lD zf;oS{cV1e`j07TwFpfHn9Mrj|fXnOk=506EmZ$g8mxkSx1_#1GU^PAHpt#{Vv`_Sg zlD6u4(IZXeY)xg^eZavhg@(xjUKk*jVy_UqCHZh~5k1 z9eY{`C6pzeB^2I!+i$5UNTR&%7)cd3(T9;!We0RdLX<@eB@tY?X|JRx*e|izFTSq4 zsfx~()5WM1xIC~-0i<{3S3|nyZ>fY3)il(!WTORH0h^iR@hk4X4kTXUj!u(+%@LJP zNSK`kX>5Wlt2o;op;^rw5@iMGRVZ(LyX_@(DUH~F&s6pXaH3hhL~{_MrwA*oPu% zR=npv|B#cnQ0%i15FckR?Ee6|8km#~$>LXZro!>2^Ueph_^9N`F#$Z*mCfJAewq$( zgr?WIfdG*EE^!+8yd1FP!3N+5UX2{U3!LI2r)eec0L=VD{&$I4Kzupk4XnDX=no}# zokChywkzLK4s2b#=PqA|^$FYyV{aFRB9=4TFEK94B{gxJP9U808LB9$Chb%tjVPAJ zzc2*+^_yQjV@cWa(@LSCZ}<;*@UH!IVi(}Y+;(}$Ox<`92X*JNr&a(`(7c9sRr3R+ z+)fA18I3X1$O5;Wst3(9_0rp&%4L@YhSkWms9NUUrgvG{jpo;djgixSLnYrSI49@ZBT?j|q4`<5?lp5Y1{@RU^ZSzBBLT?$i@)7!l|HMLYTq*##1uNR zE|YRdX-7-~VpB%u9!?*R}qWsPa@viYvG+Pl44(xT-fQw=D*6yVrDNy?&Aok@Me~b z#Xr`~g_npv`X})+o*#Q<6FvhH!5$+jwrSPOwYX6bR0LtNAiHF=wJr6Cw$lhFXJ-nX zXv~`8S*d8ElUzyR8LPPW?x~%2$yPzY+tDb)aj5YZ(y&u$to5~W9X%i-41Qlmrd!Y} z7qnu~$$2bO?!hryTRMU08ouIexHdH3)fzgOK;eMnb2iFtQ+=p{%v3cx@JgQ??cSUa zO4SsMG6>^G2o=g;5rP{Msaw$83RZe@7y7p3h@7rxWoAr^xHb|S*cW+Gp4`>FC9(Ok zk91V5lrBzQ{gUn-rw>b>X0CTp-ul!3dZ?oowc*E4e_^2Fr%b+8)16`rGS1=uv&cwt z7pG%x1eKz+do#`AM4R#6WeOf0K%hBD5l|ktacbtx%Kg~3J>&etn8Q)K;U>W*tIBpr z_xe!T%&SVG9jm6)t&#!O2Wrm`EIAGo(bN2VQ0o4Qft)KFf z=y5icGSEPz=&h7C>y1JvAl{0iqM~B1+K@0dL4yf%3A#H ze*yP?pg8{b!ce2}V)WwUmk;R~!a4uzQMxwHF9|?%F3eT{rG|n<0T#b z^&G6rLROKE*cpw%JHfnp>tocQqIjE4T@~j{LrNrfI#X2J5`B?mS0|1JY%f?(fKPuA z_OlcP^@6HwEUj36p{03>S%TC0;wu#OX%@n=xrU^X^34z3s{o-~_H%c1Kkl}Btr zWEsmkJ+LV0GOteZjQHs2yhZ}2ZR-u&O+*S8Z(xyZ+v;kFUpk7a=l3DN;5Ryf3{cI zMz!lYw$!zTY7q-oU5CM?h-X}qh2geVF+ev0!;)KX#xKy^@G@ANw6^&XIsx~J2!+}S z>i>a!7cXO#+0_hzi5Ms;!H8W35U26Lq5OC&Xt0Oy zT*KR0c3t>j|9?6%#>#j-@7OFXOTfKOgNug9m>_ANFP?7})h}kd@|{2-*Ri3|O4{O> zcxiGVQGuoGhVYvoaW&{Cexm5lS&u8`OV&%4l49p?iWuC5)rQOV9A}0 zhZ>}`Hz^Y$Wq7ZJ;$N)ih`Q42bTXJ$mI?{%`YHeKnZ*CY3X_pehs{kqVlqkzw7gbj zp7f=A;~s)%_z+KT>mprcBcDZL@fl8wH^>fM1}rZ_h)SrXsE0;f(TlonOBYNzwoQi= z&N>VakFy`qpFV{ICHF){d$UT;6QPBD(%x3t5LB9TJUrZbt`LJVvXlmGRfz}3ma5?w z$FBFHwk+yu*@=z}a4SZ}2CPB6LREyM)*jd*6>|5H*0a2HC=#i$kb8sWs4JP+nOsU8 zLlhEv&$k+mZ5%PA)5B=ShvSY|ZqnIU`;}Oy_WzhdK}o{bIC6YYT)> z=83xX1W6>>1lkgg{2j$0*XRfd7dL;TA11#A)BBeAI1>QnTGhGNTg_h1y%NO`s&p?c ziMHA~!tgOjJfALC>-e^Q7pB(6NvH^}h7NXJ*K-9dWy($GP16x8B`!HE<(Hns7}Ei^ zxg*6Ah^SwSGF-_a9l2BF`={5V19r$DUPWKUaC7)gu99yOOTuDbj;K8DkNFXp@Owtx z4^)oEHo-S<_t`OL)Cg-opOONd(QF{d{NN+^UB02@)YhY2A_Sm9R&Z^_xoTTFc7E%hMfioR?`}oU_f3z8(OU4Cis14}43oL(2`bOBJ3KDw1iD z>3Xl2^!FTdm?b?JlhGKZ-(;qL(#pP1{)da`^PG77PAR^8f}FFPiC21(`vP8Yoh{!5 zkIYOe!06R-X!8A@Ob2Khixl8tmzXTV#=I zgEr2!t>y0}en@s@d@mvF=W5EWfIqqRws`W%b(+I=RkK!&kG9%_;Wu5}*(iUcu2r9P zxEZa`VxcJ#;7w$P`Tu3l@sn;U4mZr2ALCYcl@c6AxXr)l0ydA%kNCAosk4HIBn?2` zSU){{32%}uLiDwqiopZU%j?S-|ETHow}`#0hD??AYU7CKrS;}Qy~=+-iyX_!%YDrtDYi#HHepap(?%_S^qCE z1mY(k4vxmey-RA5TuAACA0=P$&G9^DALCVg33P*1&yN4?cTTMxbGc4q(E4vHqmaVb z_QH_KC*Y4>^y9#yjKa#nj;;Kr$V=xg%8q7Syk|JXDgj~uO&SY7*8m>aGmQci?9v~9 z%_p8lqNmHZK&;dIIR~zq6n&-kw>eb4(u9!%)p5uL&#Al;Yg+-yojex?3SESJq9p}Q z*5c-nMB55j>c3|q#P4SV3rGKj8R*jpmA-&K-7*KF)_>2$YAm?hCr2|wc@I(%RVJ5y z)Q?FtFMlx#V{m-_LVU2xr}M)a8j@G;JZa-xVSx_cl~$D|&o*GoE?Z`X)fpkGF43qx zzTXtKMi0NdOWKmSwmPtgR@8iV-5d@3s4^zj;!&uxPSn{5nR7J?n8f6DSVBIO4&Mi= zN{Be+iTk|zTK-S1>B&{sypmN36RXb$Z&o`PIUzDIb;DtUYi*)N_9a9)1~#^J9QnHD zAL~A!W(s)96K~jHz!&j_5mjy&)6Wzt!4iM+d+@iF``h;FS?Xwq#FV4#CbFXhKL*ZO z#WhP$xaqmv&KtT_`nZm!8(-VH#BIHS0U}m0}FpA8IX5uPZ8Bz7Y%bq`* zR@zuUl+dbyD@q0?ooKA}_Y22PUV1sB~9r|5M7KY}I3e|0?jn+eydpVzTJ zZ~BOV3>nS^f0`#gc!Ym#tmijno4m_~*Br|G_e{9fRU;Yq3bUl4#OE$?S__}D#-?^; zbiSJ;^xnB@-8j@NFC{9~m8)Uw!>>C&!CdS$lSE&8xmZ%PtNOR4xyjwHO`Un^PYQ1O zSG23-H`UpGv-;#AX+x7&j$ag-5v>1gBpzI&8x4dTZeN^v-bZg%C#9LKG6=iC>eYw| z@qr3RXXScurBUDnJ)^@MGF1T?Ha#NT(0ZUa_bpceUn^aIaqJ}#tOmmvFWgtyB03xh3w+p5ja+M=+xK9m2!zpFO zqa!gd=F0nXuejrB#nspR^Xq2a28?ZP6o|zZ<(J(3RJIhSV)cf2_3`zVqqtX4w_vywLg~7u#6YGM7$gbjSUYV%pZ1cag;SFGgS8BiQt} zi1y%eNMT?vMOBSl8K|lw_X&V z0b`~?84v=LLKe{42AvO?WyK5|e;I_KQ82YXgY4SW zW~91h za=qq|)e5DFRQTtES#6-YeD<@Rk~SSx&%)UN*|MT)!x>|lNfT{BW3`W~Q+bIATG}WL zlUk}VzcHuekjl^IQ7f-N=3=f&hBb<~@)~F5?iHjvX&Egwkr5M5)w341x?0>(p=WM1 zNh^rN)%oYkVJ#o%*Xy&YEcX)H9d-318PC0xO5Y4JOA>Cib#gV9ZBBAYBji=e-p{sI8O{W6F(zX+Zb2RiE& zVlAt;_pc&j%c>v~fc4k&d^{;o*i$())+7JNV*FOpI_8U^R6;@rb$hGs>dkj^iAyia3 ze>7G06n5>EpQQVm?d-$sZd&g(dj_WlR;%MuyS}Zze&d=rV42|jc-+vUQ!PyP%g|C` zZdkTedcz%Et>;2#$E6#%BtMj{PB101_?I6}Jm)V@c;fTiJv*Z@qs&9%eNAiczh`3d z7E7O%y;+s!8=2Y%)>RuNHkwf22@AsRZ#lUEyeJ>8ZMEE>>Bs)YrEY-$|AC z-{*ZaejG}11J;+-l4+d56Jt8zdFSTpOEdNju7`=WO-Zx>Iv-9;nXo4Jn;vP%HscIg zr4v*{{}#x2#u1CphLu!7(^=C?uBK3Nxi4Sly8DAC*=Kgfa6`rU=k$t#%hO6NsUi+3 zX$5>$y_ou4t>sTL*`K$)efP7wL_gAMw-Q`E|9;<6ZJp+iOSnawff!SR?Wl#1Cj@4? z_4MVnB14mosfNGI=BT-Hd)Ak)fx2&NjJTLisYk4>jc5gr?n+J2q#w}TI-T1>#svzNA`}82zUG(g4{`()& zrMZH#Os=mKNn#T7Dx+Vo^6sjb>j3!bAAY=1Xi;o?$|tYdJ1>j%KG>3_8RYzpdYsUW zQlAMaM!|;OzU6WD%R(AUc#B2eY(?;M3xggnaEVEPO9`_uWwySws(!SzVl*8nCN?c) zNi9my8Z9@JHf$_U{46B=RmgIy;o(@4h8~!~L zB9a89AtWjg=XaYQ^E2O9g;n3;;ZKJk_;cs6W5&ZVYjwphHq8a*_as}b{`<`(%vE}M`E!Rke;D0Ch_B$HpFH>6VoYT0 ztJ%!Sd`L*0*P7%5%A3FMhwRw3Kr;WT!QlZAlu$fz!(Fb1hZ;Y*ZUAQ?ft>+2Pf!R(SE^!AeO8#Eszh~~N)nOZ~zSUZw^G$_rsB`5i z;2vJg3fd^eoaZ22qDZF3SYuqHeQt(syf#RnR?&Qb* zc^>~L4V`n(@NXt;QGeAwR>|(T@b8(5Vu?00bcJt4X`9adV3e^-aQ?h(9ETI*gVX3L zpHQ_W#Cs-2-#NKa11fMc_loCuS7MSW$gD7$vnr`6)>8{NIk7i{{L|e(zoK-{QxV6? z`NI?T+N01DY#lC*Gu(~K4ljJZQZ%z{O>J^79i_fT8CjHB5{-;lRbdu6ePUweB`Y85 zL&r{Z28?vDQfsL`3DUuyA$ecy@%Sid&Wb~)_^Fu z&vC2X2^r45W52RI@}wHF9^AyGs`Z@kmSOAQ-pcp{6c@cVY0H=pc$r~VRlh>h8Y*hA z)-jw7bF_&mjtbyhUYqzTt@=02@|wSIZ1Hn{b-VYt5c5xx@AFz7#T1hD9PgN3_6aVl z-n8&GO9kc~a(|{+&trmqN6y##8FK|HU_-fbs~9mpiYPfR`4Y(S0Cu$g^BbpsMD#sr zhQ;}Tveyp?|cB^8(IyEkdasT)`2>2G_Im#LT| z-Rt<8QD(#&9Pt$>vqxb5d#0-Tq;cw)>VJFke*TQQ=->J@G-Bi4svL{htEE$Ye+uV@vl$0Gb!k)o~f#L z$0jl)qg?NPoSz_2-s{T8`ROv_u;F7z!()f)JN# zb*jiUWZ6Hvn{-)=8(z#hQibz!l%=75G#uYAB++3)C z1?o+&wVX8{8ON0uW@88JAbb-LAOol`@z-3 z`z5C>t5^)@KS}go8WZ$*E#=EN9eUu&hmV`(>H!HRPrsQx(4C%9l}!#m!V?;`A!qGhf}@#kOo(Ti*X0&V8(jA5|iJ{A0)L!$# zSXP0L+m-iHWP7=YY?R8Us)WlG;1uTp>q%&d;rG`_@AMrV$?Gm+Nb zGa~CLE!%m^G^6@a{#CV!?c$Ff|DM_Z^G)xgd>xa%BgS!q&i9&w(}8}hE0>UFkEj~& z0$>!%plqt-P`%@`(SEhS)VAY;lb74a_gfCpKaQ(f4#j^QSFGqpJ~^qW9pM@pPAe!K zTV15(%JrJ6%7y3h=YGnuHmt3Xwzii}`J~EL?Ld1XTkxT@=U1rtlo`Y0$A^H`FmKkh z1%3TevQV`Z-FqfO->FjnZ&=M3p)nT@d%ZG`h?XSNj4z8tgd8}-@gpd_{9t9rQusJy z01Bi4+?#)>aZUWfPQ^g9m=u)1c~sIXfWzyNL8l`EDZxtqeJAw5)3`B1HDqGb$R)#l zb;WPW;Hyo(R<^{}#Bs`Y&B>7MsmiJjos<}tWE4gm)EVBKgd7HSEYObPdv=fZX8|Q` zjd1ii>1&DbdT=r&N%i+wtu}fL7{9h*To&i5VH&NX8gAj&81c^F6lA#v$RB}!Q3X8PrarFk|PNvtgs+qTYRts50yp25M7 zoR4=S5`InpdnO6DSv+pF^J8I7#WJ8VmvcoYzy%$pvv_^BJwi-j!9l(HdwYHW4V~EX zD6u@3|0U_ucA1Okc$G$9SGoaxTV20xtx1mVGRV7~t)9dIUn%UFqi5ZTFn}z!gAec`KVeJr)rf=}M4pR>?1klJkj@ zDLmsJtrl%}UoA9vB(|x2FWZYYur1qFTk&J4M>~YB{tR$7GTWWO`^+>4{4AIXPcCa6 z_8qntviAg)l~**oD@@`w9F=Rda^Y_sJZW1OpI2in)QGNuFq1w9*9Uh}o{G+3qE{uJKq2 z6vxUIg16IYELVAMnD!&g5I2Y79kIm)j=cVi0el{!GWrEz7Hj~xW4M;sZ5GpwO&llz zu#YtC1*0p0)!i%(yb{Un{Xi`&75ZD;4@i%wPKt#^aPWgJZ9H~fjdEJ;U+a_1;{E;Q z9Z<75dcNDxJ29g^`n9L1zQ;t3cRFE>bVm%Tjm`xD$S^T63d4BD$<T*?0p51h^A0TSTTo?S;fQt3#x^j}{r@G8B<(vJTEJf0l7$LeV712ac%&Nc z?2P{=07A$@V!Y&%^P=r|-9!t42h`;*6eQ5rkwc{a=e4!ifH6CNe7ovi2XG(;{@;O^ zv*$0IKX>Nr*>k{o7~nt*I|u?+ItRaH{wLJzZ(okT9zIpPM~1yfE_n5J?EfB$xu|<) zcQ#@dybaWZla1K;_5I&7XQmHBPXu}^oSJBr_d4c3j(7j4!FO=aQ{=D=-T2Le;oaVE zclnh6J%bJXeQn8(BJlBj6X8UBKr{3A11z!&3wxyOYM0-|2Ok^~>|sN8-8UYj4^|(< z%MOkWJ>t1hqexQD`uKU18>g&%ZE5O8jS}bI(}RQb&zslmDAhZIiO#G`e>(N-X)-B} zn>>{1?`V9$jhZF`uO?}Tk~y!StU2tJ)kMgOCTa7LDU{65zqp3Yd+t4&nr@sZJ2bq*$ z?ySYyBU|H2&luY+&#!fq=WkxCbS-j<)(&jiE#BwrUzZ$_%EQc*1Lv8Y2Q(8s|GwMh z(RAAB@pkH5W(fDQCX&{Zz<{k=wZBbo+}9VF>2WoR+C!&vdU)|@g19pVNiACPusQxa zP370VMK$LwliccxTzH%net)n}^!K^fg+)_bPa+BY2XO;y-!@DpNS`KfHWR;F@9rtu zDfJjGe(S%}CfJhE)~n$y6(oikm`>fJehn(j^fh{w#eA?TKp|5Evh*{=L2Zf7vI9F& z;=00D%Un=W+8-H$jgIjdIHelb*qh)wdnxc>$0eB>kDf{Mb;$y>j$vt7L&)o_ z`Fa_sAUU10>~aV=>Gck&4z8In2m>kp$7p4Ri5JI229|5&mk_i6@7wi_VfvlVZ53mE zitb~&{K6!6T10P(a0D21G2|%a@;mkEgEG2Khc6dydhi&7(#dYXjY{fw0{zoMuIc28 z0U3|NlxWqpA*Jc4Hll(BKs?wm9q)hkA zR?l~-r34iTXSmp<0k9!x*}(Yjd$F#JwNBc%x|54B(HCnI_`N;iS~y*rRDT*P_!|n* zyLbG_c&W|pHRsD$V%c6}fC^j*e6@mN<4I8~d?BFB8p(ufsqCYZa&s2&^{7(nP1=0f zPQhXEn>=S03&RgQSLI@wbuk|S;eT`y>ZGQwndq!A!3CV?hskaYh|x?dx*ad!ad2#1 zUqbGkXp*0m1j}WcUrBwU_{Z3OB6Jnyy%C1wp z`$5XF#lVXL_>Ts`1D`3Ot?5O`K$GOS6)CT0!QUUI5G*B6>UxEcr6K8XmAfi;6FBw?-Tvu} zHYmr0-WsVXHwCn3-)vxhL|> z*YleJIO*>rn@``89vRJDC*E*Kp?=kUZg6O^Y#Znw807M%C6OCAZXi@v3tzK8eimy>vN)G8fH!oZ)zM~PEG_}^3sMhr=XgN=N}zXZKm6?m#?&;i z$VKHyxH3#Wrx>=f`L*;@Pl8%8h$c&USN8oaf&8mzdRG3X%<(nvi9OCtb6@FiW86g* z(2EJAM)$<7BMkC#xq;`jdE$Cf%x{oGSGb2jioCe~Qcd{#@Ev64t0Vr$r60Ez>fTpT zW%*Kf?!wX*T_*-f^u8Jb420G>d!+I4QObL2eBi4EX5k&dqLkjvUr_trPQHP%@+<0p z=9v>x=SKpc*!SdHnAzU|ao&3}m!R}sfBftCm1(0-WKkeZQ52m5)hrx)H~6Ib0T_Br zs0kSxl2u-9h_X-3Y!dKS!L-tRdxS2dlONRJw{Lfs_{Vm}6%)LH!EcaIIf)c^pga9d zbSIYgJJAw?nQ@IC;S1?vaPMOa)VGTsDGe&!_W4`znZ#A%3QXpSxeGdDP|E9l?DB*Ll(N{z2GWjeoQnkS+cOFOs>xkx8P%s0=<5wV(broOirz# zMz@ssdk;LEjHRtVqNu}%)=@G zVI53*ijZ-}?p38Rmx6X~z(B50KZ>g>i9R%xh{P$qj$3TQnTxkY#T-K6_{=6w^f{_| zpq^z=jlwgfFgvH))27BEbV31(152n#Ehm}P=-u_meFJIJ1;%u!`UIoo?6YNgFl_s_ zvfpM9Ok(t-MYQY|>92;h@`EHNr5#xq5b?%BYL)_(mElb_{Z3bol(<$7f(|pHTG&u6 zM7D@?eDH|#9#8B9mp}U5QG-I+TiFkE@lsHgaDZU5sy_31M)$``SNGRfWc@dIX_8&3SU+IAIVoo<2P>AE=7@y!K^9 zeVT-BqhC!##=!9>EEZ2x^!>KT4}3vfJiX5@twoFe^)dXyRc(GjUt_aCSG?QkhLiZ8 ze$&fOid@mgFP$zz?;l@T$R>N-1j;8$cz+YAU8~0&=h$nctal63(=eOb;B--=`4ou_ zw8PHS1^?czc^R-12xK1m7{l@1?AwD1IXo|!`tInliT`Q zFkyjaYWKRva>MesOZWjp2O=RWQcWF=cePs7#G-f|r|USqozSuaL-+TZAu_Nb1-|cvD&C0ML2@^|@VnWCDugZW3dAm@^87p)r$@fQkk$60yKd0h!`Y#6nA!Pvw2^^|E zcmCWTz|Wae4Ee`d&^dOn()m9j9E$M2ptsB}aNa|3!Ib|4kHE(+oGFZ3D}e3-@3lKF zUST`7jSayN;+|bF1bXAW+Kc58nMAwH2POVewNFYj&2G&1;g8CMq?_iWJ4+MXcW|QS zD9+OdF;*bGR@hcj$WFSkba1j+cQM!WeQ1Gampna7WA#$u*!9MHYg*56avbZi zG7e4Pg|~lU{`YMjS8zzejiG{=0Lk!fu06Z38sT_U(UZs`%5~^7{y5S~f0+wDo|CvK znJXG`BSM%49*?g25jItI*|Q$cbaF~=x!onFn97@&bvGaszsQ0!#q1&9Dey5N@zasp zLc!}|{aSt(sC*u>&tFc`IBi{md^qFxC9B)JXF=`VMNhf~%zF&hELNHFRkh&8gCY9Z zd90h!((S2l6VSWIVl%yE%q2=36 zlXNz0Pq%;_4gKl{5+cP#&Asv`EqpPk&87mQ6_V=srh}I}v9ar2!{I9bNTDZO z#ANV^hmlyyUPXiZVfAcqm0|~cK2FO|!hCaR3m6xn0eRV1PO?xwR!6pWK(@m(PO`cr z>G?*#qMWi{MkBJys{tBgCn9Pgfh3XQgDy(Y_KGC4ku8pXYfiH9(7{Jk?fTsZ$Ufet zq0Q&7pRqac)Zxq#lkUewaxy3?Z*;M|<2g4II^{&O9r3|jtyWR8u-+sVIJ)4}`xBg_EWvRqh?aW9OC8%r<$$9~WWiWcQk?}{0I?xC6|vev zky(}D`1({4xsF(9mXg%#*Ga7}h0~y2J%bm(4sbt6s%%eGR!^&GjxXm0a0AZnjeXKb zJ#IpmUHWQIpJ9^Or$O2L6!v1furkj3YT3rk|7kVXr(M)uy++(wqzfWzE72v+Dwy<% z89v8pUSHUe9xlM0^(GYmefJ}i;eOE9&z-`c#e5q>W6Z_o&9~1(@6aX_Q;7= zcBwYXf5s^mwv2Nk;|{$}r@{xAid7|+o}2K{iq4tb!+sEdsH2?XJ_Yu^*A$H{ks(uH}8@XRs;H(iWY!LvJgEEA?vzZC|s zWw6V7V={vLKQXiWACl)I`25UqBdEcP>V>=n!dvBX2eahl`q}|?fuW^uzHTWLQyX&x z5hY+KY2P^TVPG;41wyPvybWFlKCqZ((}=j55=mH+2sTG?FiQM?yFDxl?-*S)m!yJ= zjj*hXH)MRQv8hTY7Qn46QvQ-7(eIAy2iQO7gyO$$eaI|X4HiX`4-vv-(ElC*se$8B zKI#Gqy{TSmO3qGQ-BGtMJQYrn_+tgdwZ?SGT$3~Bl%G1f^eLLgdM<&N)T+9479mWz zc<(zcBC3d9cdT$WdC4irO1-sZmOg$L?!PLx&)4%vM0ccW9Q(%vsKgDaBb zaX$2FI20IKuoD({ay1cAWPg)EQR&=TJ`p%y`tKRD+QCaJE0L6rp3quXh4d30ZLM@< zk(${U$^&qIV{wH&wmEn42cH)ffV5yR0s-89IGySg2@I4lO+KLd8!tb(Zl28y23(g9 zXEe0WS3bOX>~c^?V~a!e9X=F0q{qP*{B;{7j)G&T;#uMLB0W0*SUVnz%x5qzDIb_- zV0yP>v;24RZC=7ResSur$@fc!kGm{qX8DrKz538&$l7|8vKJ?B%@y{VEBq+4=~BYYtTlWwCbr!stWE0;CzYk~1f$;qsM+u7|>E z-fDml6~+M%SkfFke%C1fWjziO!L_F&04x8~MUn5OrY7+jzQi*ai-u7T7LYti|6fbj z9?$gt|IejUNyTPiu4gkthBg|?IBhd?zf@wUICc@anjQ(hHA$v)*#KTV_i>xI5j_qaM--3Gm>K z?m(qjSZQ0q1TY@XsVnE&UOE{qqZ#MA>1oMT=FAv})PbwY+?CdS{Q=IHV*-YhKjXc* zq3oMTHO+i39+2ZF1w~5PDinE zd(WzC(e6z}3T@}NqpP3ox=H@rt06oWY;J@hc-Pf9!dGR-%9m;{VVz_Te1vAT(Uuj( z$oDI0ixUF|*+Sdas3}yK@_j)0js3q}80a3~yD1f6DiFLK?`z`T_M!w(ap^Ywl1mwi z_up)7gSVEcVKHe=2Nv%DDkySjuSv}(b%6q+EtCL-w)>>?f=UAfFf;d!r&p7ZoO;Jy zJI`GGcb#7-gzl_m6;mAPZ1Bois!Wp0yW9H7f(cN*uX8f7W!!P?kLK***5dydYaqiR zbZat=q`j%9JP)8jm^pI`!$6K^C{HH_BtmCTW-h9{IAy3{1n6t8 z*(r-h7bc!ONA9G-gXNzrHE=4rM=t1|m0B+K+I8d?t_1?ig~~T>C?`z1))9;7uhTZF zk&Y{OYkF&s!9qC(gcXrPFrwkk(8~XA5r?z1!CSMSx}&ei~9mi?*MF0^@2xS06vAKN-7`jou&imO`C=2n9wS zIFvgbbS12obQ-9YZ84fjSW=adg5Ls|QQO7mC=zD?u8{-&C|#(qu2yNAdY`{2$a^Jb)(Z;R`2jpBKnL0KwqHtj zC%LiZ4u`D`cOAOtz(&&IKYi>5>0)Nfe>-j@h(k}_c(z|~LK{NlSFqj~gYbB^cElc2SV0B`5!ZD`W9{r2 znI8YG8cq{_DX;>vkJ##hVkEVk({?|ZhlfD3ZzNUTEOz{hx z%v+-{U=w$GEQsb^g-iAS zfT^pkx501;?lYUKGTNY(QS>!VG5dlrKXI4pj?Uv-Z~<9f)JGUivyi6h~VpLl-h zEWfAMIj*j#R^R)ynUFk9AxMcOb6i`XbkK&k)b;>k^Uo2f!r+NMn&-wP(Mh(Hl*a=x zFy;L@1kH>zG1tIUo{z-^o*CHR$Ghr+E$g`R8P=)>0lcN5Lt966wwzx#5{})qqna7+ z*YBq2%V-Z01FH@Zp33zW-?j@`H`GSfkPhAgVbC{5#6-Yy`W=ml{Tsb5_7?M!-b`2; z6;zhnh)ImQ)T3pNeG(sf1`hmj=wLZDYvgs<2a*Fg`q$V)zw;#ppN+RPR0L$t#S8!Q zg8Z~C0=Nf{aw7S>fAC}i;E$G+&Fhu5ynboz5(|6xC_xTb^J1X0+rlg1)^Kxo}S>PYj%ZW|nfJzHw4q8I_M+I627dH!Awf+_<*f}qOO&AOX ze7k^GKUj3!U#tiv4Tg$GjM*wAOaqZ)00SERxjqQ6@^{#bjb9M;tj7bZh;ADH|M#|x zM_Q}DBVKcANd9#g(6XN&PQZ5-xV9wke6Zg@ zKoz?5-K$ws`kF5cCSb;y$Bexm zW;X-QW9PqA#}N`+KC5_bkzb1QDpHc`l_9=n`wtqKeieHMOttmu+N^oL#A_|k(**8= zT)Z3?PSa1?`2f><_6du4Z5RHVE}-Jo{hP)kQ6kChGd-yBvwDPQ)>{U8!Dnrar>S8O zcJ!;}y88JMnfL0ji#qrIm(F9%%9nFr!C(yBPP*y?*iJ>sijIS|aZFapF*{kJ^y=r# zJT45n64;vjrQ$0pmEppA*YGNR0n^zjIfM&((t|qH|3E=56jMQ20yK3YG98_`9=ni% zm9op6`YUa`cl&zoF~*NQW*V35H!{0@n%wma?tjHl2yo>b(W_vLpz$>8TQ{t~Avxb~ zl!^1EQi1!Q24Wf;D0B_<0$C=zr$0yfN zPGgbH$p7jeb2QKZp-2N;N8glUd}kO5PmNURUa14@-m~?&zBjofhhXv5n-x zp^PFs4q{uRb@EQGi`Ih7pPIrDs4`2t4@Vx9AN63E3hw@NM|^{5>>U8}W}m685fFts|*UN%L0w;BwSlYjYo@o0=1 zq-8Q?GyYLL#z5oT@9O^a%yRjxqO-18wR3BOAsw4(N7m!K#be<(gWuj*onY+r9P=So zXBxotyu?oPOc=yj+ZhHLAdeZ=y;NfB>iAzvQOQ%rZD)Aw(tx>3B(H=M?+awwn8$~lxqKQ`JF!)P>aM7b9}uEEahB5syV82{mJ)nHcL`5z2N# zyE}lV>QhFq&6CyO7Up zkajyZCjaUu;X9dN;2@6zs&I-ktpxTMSaqPjSjk<&M zzz5AnBhrdNW#j_I^&W_1PCciu(^AMF7N7CPe?-!lccx_ZXdWQU;ZH}=M??q6Km;|p zzmb>-$D3sH!5Ub9H*b2D5bwC_hYMmdpu3}C8E*MI_K}Q{pB|ggFNWFeclWv}5(AEa010zItc&Jo)4e z1rnz&aH`bRsL&I*0XDPI(NA67=sDQVj?G-y&fFa#3McK)iDnQ`2lFOs{RjPY)*Ty+pq5Be_S`- z1vFZTz~*{G3@Z>lsC!WW;G*uA&m*YkI;#$3q<`9M8#(Vv{yl*gJ1JN~Zy>JzgK%BCDB~>GNoxVpgfP*w3Q0ARI1gB&6f-z4Y99=zK^?+?!S{m1 z(Yj@g-mn$x1=yWY{Y`TvkQV%4rOLAmHy$fw9SV!Rh;-O~BC2cYQ8)hBgQ_)qhY*2j zuRr()JSh(7LjYiJ9Ec;&Pr^#bWaaLh7$;PO_sM+}x#Y(N7v1GZzjT^j4Lz|`nb=8v z8=$?I8JBk@Npj*(2K&`L`58(3Ajm8L*gV?wj^n`Q4IqwZ{m}xZTLnChb2

    E-XHM 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 Date: Sat, 21 Dec 2019 17:58:14 +0200 Subject: [PATCH 101/227] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5bf74d5..576bb21 100755 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ LearnJavascript: A list of resources This list originated from this [discussion](http://www.linkedin.com/groupItem?view=&gid=121615&item=5909742634301140992&type=member&commentID=5909826380652113920&trk=eml-ntf-hero-like-my-discussion-cmt&midToken=AQG2dgaKId8xLA&fromEmail=fromEmail&ut=2sQj9Dud2coCo1#commentID_5909826380652113920) on LinkedIn's Javascript group. +![Alt text](/img/When-in-Rome.jpg "When in Rome Image") + ### LEARNING * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) From 8eb1f2df60b8119123bdc82af9f89cebd7cbab96 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Dec 2019 17:59:48 +0200 Subject: [PATCH 102/227] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 576bb21..2efcfd9 100755 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ LearnJavascript: A list of resources This list originated from this [discussion](http://www.linkedin.com/groupItem?view=&gid=121615&item=5909742634301140992&type=member&commentID=5909826380652113920&trk=eml-ntf-hero-like-my-discussion-cmt&midToken=AQG2dgaKId8xLA&fromEmail=fromEmail&ut=2sQj9Dud2coCo1#commentID_5909826380652113920) on LinkedIn's Javascript group. ![Alt text](/img/When-in-Rome.jpg "When in Rome Image") +*(Quote by the lovely [MPJ](https://twitter.com/mpjme?lang=en))* ### LEARNING From eae9db6705d13c56da0f1e59c7415517c21906b8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 25 Dec 2019 22:31:13 +0200 Subject: [PATCH 103/227] Adding ts-check screenshots --- img/ts-check.jpg | Bin 0 -> 28194 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/ts-check.jpg diff --git a/img/ts-check.jpg b/img/ts-check.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dfe34b6138c1d720fd98077983af0b21d6a1050f GIT binary patch literal 28194 zcmeFZWmp|slLp$j26qdxaSJZNU4py2yE{Pw!QI{6HAn~q5AH5OgFC@<$&qi4d}p3# z=FT(s{+MRdRrOZY+iR`8cdzQz4L|08tOF=gVv=G27#IM6J^uhdmH=S@4jKj)1{w|) z78V{J4gm=p8R^9fBmxXfRBTcrGBOe(Vq$U{HhOYOW-4N027X3n4lW)Z9uU2dm>{

    }Pv_-@qUtprFBFV4uBs&yHWV-+j-$0*8Qvg8s1xAVNH6qClWLyKXF!J|?dJM+^YK z{bqmz0Js1E2r#SEz55ONs~9m`?nv(Q17LXjKYj7mdm}*%NIb_7#|V(rbG0+&MTqsXxDuX1H79yIjmf=KV@_ zYd6^O14z5RxEfvYrQF$__8$3qRIcqZ_{i{ctp2r`)nB^JJUnOu5Q$&+ZmP%bHp$-@ z0$8SXSWabWBgbB)Q&{AwhT57VFU-sW%2m6uI(F!o_SaY4G+XILvR_!W?YeBQM$fzx z_!r|Z3LG)6-1Fxzt)>%VJSQR0X=<#z-&?WQw#CzV#e2|nB*5{!qFq&eHY4$$ za{;3LuR;KBIU?{nT5WW|*7LTZ-3MRBvAj}$w;i;rJ(j+2Q@c0D@B>ILm+8RpeNSXI zwsnVFnYQVYmHnw6IlF)CPhEUwN9_Czu*~B4z37)E8r0-kozr$;sOD|b+-6qYJnS#E zbskKw8S{BrK3KUs=BcRTlTLqGKb95omv(`_2_^=cxVgJn9n}?h_JRDKcYoWOOVf0m zf!B74ODT7pxl@1h{|-pT^ng)HZyt=?dwBT$4*pg0#u@z;`h!ikcTC;+2m8+|7znp~ zTJ6u+A1yfI_=)vD0)N&)0oMi6Qh&z&WUVYZKRRtrH9E-{@Hc;;b!hs z;o0T1Yl4^4n)mUl?|XWjf6L+w09w;mf(R0TkiR0;-;V=8(aHZC^#7p$WCS4SvsJGj z1^~dK<@)w}^7220vMfL4-0Nk8;UN))bN4M(lMHx#d<%PEK zV60)CNuaz(XnATQ9jdCwjOwz${MiiB?eeXs1uxI~AG3ezCgtQf;Ib#Z$CVwfOdmp9 zhPa(NvKhXQtPxdRyq8P%;m=;YZJapUE17lmm?5lwc=WD5zP;G@d0q9Su-mV6;i-RQ z<1j~g`9a{X%(V(Q0Q6k8c5TD%oKlC^8#}$Jv?iQYY7?$%@fTySKkPc+r$6a1EnC*R z5M}J{WwmK-I7EI5FN$7>O75CU`hM>B7e=seL;|2e^&GH02>|OVY|7U_wZ63(YRsgq zwWU{|W@N^D?62P?Xv-$6oL{Diij$3NYmUBP?n9R^`9+dpER}tnS^VPVUyrM+wBESo zdu)C;OS`!B5}9M5)|kI^2M!W=kxg&aW;P5!`U!vl zL1L7@pYp-7>J#+AmLH)#WA z){f_?A@11$M(p=*-2VstyAeP(bJ;%=K*?dd*&95){HJmIz48qXfA96kwYmyJvUBx7 zw&PRj2l4}WQ3L?MlP5q<{IBVVbNmIq&joXE`g_X}XPt-5gbU9n>I=Tv_92Zg_Q0a0 zK}4+LYOzK+hT54u|ezs0h%l0(E+w{59 z?9}UEVYWmQa4X|@&EX}@^q!e2E>SWYQx@uob7MBHn&0w&7s%prQ=yLkRoU@0zs1d+7=*OEV@Ak$lrs1mxwd^`On4u&0{0CVH=lIwWO(?$_ZZ(LRirCuCzv(Ee+-q^F-(M4W_2cg?p%{$IBo=Yb%vXlq6>%a5;E;UNS zavxS-n5gt0l3q$}`I6yeN<;tNQ&Xtxvoam;Fx3a~>T;Hv6hNDw&LL zRbKKZ%l?H4EUiA_*U6&h(w(bee`OTzpAw*E1kfl+^OpO z>CTNu$Gf!DDrt?qw!3zZhxV_3Aq1Y^p<%-kKZ;loKn;MITCq6WaSCAY_PzQ`AYx)h zDS))Qb;nsb)S4a=_)E-B6Ux0h-LM`*A8hq`LLj-~oLGI1Vt-`u5)$$QgAXMQ zQHFv93ZNjV;7Bo1Sy6JpMRQ1WqnR;@;VYaNqRc_13A^b&1hd7JO0>_1NZiU9CJ)do@H*H1h2W|iIiFY$lbSu$1nGI)ypimo`14#_s}Uhnv1 zi>lHg* z-Ds{d+vLSlQL~YwC%v*+ zj(F_Rvv|B6-geuKBkvW?>hkTbQt2Xrf6+ror&sNkLIMbA8`C5L0N96BSG)lITH_c} z6Zvoo^=W~@^dtLu@Gi6f);}c4rC01$G{#hd3ts*JkZm~Xv&yTOdu>L>o=d5v(OE4d zLg$VwVV5trX;XJhZMTnXL{IL6fS)8}02nE$UmTo-{J$FTTs(6gkeks6#P}R+++7j1 ztEYC(iFRufjuC$6X(6k%G( z_RMYb>3tSDyIt5|kZ%37*=W5T_l3>aRPc!J!*|YuIqS)PGYO*>Z07(F6VGG?aEb!I zbN*5OTkwwo|EK>KN8ouSj12_< z`dBQa%&hDJ!emS_Y=REYi)66R3u|DozyQwR%lhFU;!GpE`KiP4o?G1rlL|TO718_C z3Q4%oD7_b~P2Q}N3#U`kX5BK3+5cghc1$QMx0*WnpReuP94{?3*IO^K-{a`b3PwM< zc^stL`oGvkI@!_V$NT}*NnHdG)hA;3Ct+B{FBE+t9_##$oiK?iTlQ_Kj7_6{h-1 zXW?s$Dznu|t1@-nJ#b#Nn7u{kk1rvW4SQPN*c(d zbjHbqOk?p4ZNJ%+G}C4-k=VWNE;$}Talzj}e? zo0m5>&8l%19L_I4VHVnSWomwIfc=HKAjmCBks=ipbPa1ka^-&2B&LG)&Yv%@g}4Mh zwK!p3A_#2yyFO$toQ|oD_PzkbKE6X##)^5-#7UOsh>)WSJgTE`@1|D{1(` zy+6J%Ry9z`Mwn*C{NhMBL><%?Fk<&6ZA=K-!SmA{9+a(l@}N?oS$%e|LQbG^URP2! z{R^pRzc?jvl-Isbs;GGgxv4{QK8amanWMqs`Z;{ z>yIKJ%(@i@5ywqiE~PoR=jImVW-QYxFDKdWO52s$R6%W|xEzIEefCq4scnR$20}|K zh3Vo9pszh#5M5pIniT|%G^tRAtU%K>v(gEgO*?)TV&MB*_uVK%pdl{}x)@d3Y6aFd zYJ+>@nnC2^(!-)DxwcZO+E0+xp`b+VCY$&Ph^=dP#q>3tLHZ4FhyoL{^|&{CcztaP z#V)YUF2MzQ0pj@EZiR88AgNHwuOx2w}B95Z>-VB`jKrDO1 z-!lKVAnXJdN%*pUBeK|-&p!ax5&R6D>c70oCpocPl-zTxWKz-m2&g4 zCZ=>%hG40aMok(zc!u3Y%{G`5*RBf|mgkz9KMLy!bSsC4T2Yc$l?_>upubU34Aj26 zVI4dnJ8?*>Fw)4l8ui@eb;@cP)84Ien&Ue7{}gz8`5E@Ez8~-5B|aa@i)>ivv3C$= zpTX+C0t=^p)-z8;!o}u=b!O{=Z#AI#Z&!8-Eb+<>^Ro&C#m3)<2c{G=v0n^HwAZQSatSn;T|W$8YO@ zQ`qeM#!j3=E?;35@!Bz6)f&Nogi?!1c^=o~)s-ZAE0$PCK^0(xN~PjbBT#_^cNisN z&aP@`D;kRh*YC2}$0~w>_gL}rK)k#4Bme8z+vVud zEnhdXi7 zu@m3|*i=DBJET3VN1-> z8uG5DU@dd2v)8h&j$!ktJM~0y8;ZOT$ z=GdsZ%D`W% zU_o|J@~UZOMcF`vHK>xT$tzo&1$(@ukCnU0=F9>E z>8k1Zc)2{VjQN3d>5XM-MUqyDtw_@h9Yf1H_@lw=D}p(x7D0O`YY`F8g9~iMx^3ub z&O#>ttjo|L}PrN`+2rpAMs~fVf?yam!{iYnWmuA)E zM~A{w^`|iUmgR?_3T@!H?bGqnD_PW`xQ97ilrbBf&zI%lcws^nR78cvF`sJa6xmcW zs<5Izgg04~?)auo+qcFwH71bjXcncE`LsKck7JBYo@&inR^QN+K$x0)lF%K55l8xn z(L>#ZU};yAi7BGp}e8McZ_&#<;2q zi=1}9YOs(5_RM&vjRWRlm{B7gJVUj+|6WYlMr|$59Y7LK&Bo@8fgtHnQN2(~uWp6T zXdRpZzqsDwQez%P<^@s=mj^*s+=nntB;g|z2qi_sV| zeu!WX5*76!`W|s~?1kJfl||sA1SD=+k>%RuglVAepXsPUc-`?RDuejF9x|XJse@ z&MA<=o_rLtr7M|}c>{{{2QUZz9Po_$|B81ebE573??LB56SXSFY%z5>Dp``-4g&W& zw1v_-w8bR!E}W3*1r@>E4H5_wFr5+J(h`n3*AY`LwX^6YvK%{~*ecBJ_@L(x$i@x_Lo>LwiXInqL5!yxuopv7HRav2CMMPN$`UVmJ%%;T(EqN-b8nw!T5+Ek zUk1jwC1<_J_#)e^OyR)#oMK3)+?i0vIx(>#T~^~I$LbZ^^AZG}xx=l!83UqF&Zss{ zuuyobX!^I2|CoZ|?%INRp=dN1?yq}Z{Qi4^=CW{f)=$Q5J8 zjJPA1F~E`v?L_>)0@=Uad#6xQ$IkvhHO+NeEWi6XQ#c#m1<9NGUuNikIKw{(Ef}6KUK`iem~mJFdV$;^Yw)vz~~9{ zvgj0LaI}ClMw%&>==O$Z_Z??e#}$#hRL!T^CAn0~jz!eXnB>IQXkOuO+Nt9)Vmd*{ zR4F<75k%BtVKj4K;F__C{_l;9Xw~)UQ)c1mE{g;aw!Jr1X3NFs_&^vauS&k<3L|Ti zMsXpdalX+kt~Els!GRV-v3#sxJRt5bb0OUEO)se>--@9+h2=4NvAxlJEvai?j`z65 z>^`9ElMRXulT?p12lIx8iiFml40U`t%6Uiu|kFIFxfdLCaq>8Ah za76$|Da(C2mek5dQ_vv=8FAe^Ed~)}D#$dLNb=94-J97*4j(5zR@?RPs%4`z>Us;! zc$^`q?Do+A%d{vzIr{8w_Go-v+=!~;aOi}o`2%2Q8WpE?eZG_Cz^wMz1gA(zo3Lfs zjTpX@`gJ_z6D#n_(0iiYpFed8P%J0p=NQAr5 z?j`Wmtz*NBorf_NAHR-zI1w4|$nw}EnTlPo3WO_KG^T!yeQlW{5Qm38+(ugUsh5^( zVA@HIa!E)XpQFr)5Pxmju3kzbTx-QclX)138>gkvlLvg0^<%VPzxW6-XPKr=zrne< zEKJn@x-`2mD=0BkTye2B@xCJNK{z_EESJ4CPe(?zbvfNwd;C2#WT6aQ+GP0EJ$+oc ze7H^_igLDsg?x0&bdJ2$or0wWIQx4gfesREW0RN?)UB|DLZKHmCb2f!`+fECtrcg*O~HnClh7Mc8pE{RSALC=MYs2E-cg`@Q|Z%@K5K*j8N! z@iXRed>f(90wY2@aKdtmp^CJunu~JZakb~}*!4_rJM7YTe(tJ%k$?l+_N!QW2IM9$ zBG)IaohPl0A+mgHtLqO&&UIvX-b^C3&L7jj4C~P*W0Kj1Rm2}r0~K-iW#=D`qDTMx zJG)Zc!otDe#Px8+J-&8Gw3090n7nxY?oRQWYw z_zytCjAen8Ek{kj{m9#}6S5XhdO-z_0yc17wHL>h6dk4-Zl5lI6QelOxUxE;B!pZ~ z37nv>R2KIVQNfL>-x5au#sXp|krpbX9+ycz2p^IOZ|tJi2&bJ1Crje)QrD0qh>IY&Is$R{A?y%SiMaD7nenbBI^IE1D3I@W z+knb7$zI4&P1`~wc0~=Q($0_>rp%9*>Ws$T)JvU`doW4PM&anAsrGUAl+aEODtd9y zu0TvzZ(?X*)4z4;5ENP{bkdbJvWg|UyV9#UA&su0-=phZVyy3 z1Ik6@CQ#q*n_KS>A;avR{-7Sjt*G==*PehlFRAzfKb(h@d0sg?AqNxoYzz*K7E9cD z-^80ztaiRYaJm;#t`UEZiwRO0CGu5I+tj{WUPFi|Q~Zbl=ex2&)S?~%VS(WQ^Em8Y zRtEo$Y7I!bo~uRSjhL!(X$o)-?O+E3)h`2C%*{_8h^bQ@su)TG*5-v${RImBNuOHR zNN{Oak3;v{-HnnDW_-Il&aCc7egH_hAvc7rBFlRm-1MXIP6a;|nSZ0inMt-x+B&Ca zV(m(Pi2WH8?v(dW!T$-*-&{kk`KFc>2rxSPVZy|g#o`B(Pu*vw`(F6*=HqP%Go&>T z#zaCmoaBiZ@yKk7{bEpQDl<|x<1w%?$ZHv~(krxr%z8yvu^&Gq8b+a*m}Ql2w{!f{l!;n1)rl1xl|7rcAj9ZH}h$Aj3w>mr+qYb+v36q#1lac{}CnTrirDIu*|?HyVFYNa4Ko#k5T5QYV?uJ?zr57FBoJ zk;7!k`iXAA$)s1U#Ya}f@i1)0OK0EK`!Pm_2T5E=(l|joXzXHS?c1f<7@x<*SeEw> zVC;iclJgRYv7L%|42xkH7yee3W(veu&Ljah4a_2f^DYLi!Eg*VuS&{jXncj@RV!`L zp1Om0#}F-e(!dS)du4f7IAKE%`B_yQ)~We@Pf04-FsK#B*)p6GnksN(r9u2H(}VOX z+m_ay{}A36E+cas+AB_f6t{l}_;dI_SQi&T;dGIaO^l-1<71$6ET2o)R4@Sp=O*qS zfF(0m{WtK64T=YPu9App$zqFa-y{SfrK2*JDhgr9f-eKabC~Q*N_%^HX>TXO6D%O(ivh;tPsZy=_*0CaEZjXT{3TA=MQog6L?L=l2 zQO|Q-Ta;UP(xI||u4p8U;2kC2b3KSLD?WzT%hWWPsEI`??(2&sJ z&v&?d}IT4snx zWIQqMwhwO8wq+|lrNFf8!0UNxVnv7-IjZrggPl{O{)+105_|(YqS}R%J&PiP%UAeC z;xB}8ON`-P4=VXD;J2MC#4Fy>h8QO^WW$b#eZvn+Qe=@IAhX@2vA2+lUAJPglht{G zB#?G4C6YFdsj%Hd{DKVbMG}kg9ZM1H@WZPW9Kxd(iGzO09ASun7dp$!dO6Trtzb9< z15-CIcNo7{nKj{3#7~lNlR;*{T=BCrtnGWiBa%jch&_3UzP~CJBWOT(R@_b5t2pH1 zM(=l9Z%|6aF~`)krCW%OAYC#HabDMBVC6+L;ss)&L)|t5d5N~ntn^vuMJ(mH^(J%I zs$mOVYXGU8*8?CkHY6ntc5`G%br;c7QGqvXj9Y`HA z=aeaG>{6!kNeYajqOiTjV+{@?4H;|QqWeC`o+)UO=?1qGjT-c5Wuxx#xoPHorj}** zXl1j(uIggVMfo#0(^SE;zvvo2Y#a?o(861#{7@$5?Ph#e->x-3ESIyXNOWqsA?@MU z*TBhHS?GAN6gMHE7l!_&I7ZY__)Y5i7D2@9R7>#Ex@vi8Wg!{VrZpO^`Lej1JriBm zV{v7%3zL6ikH)$D1?F4cFa5_bY+hWg&MT=dUZM^W&WE3}=DZ>eS-NVqq_)<$6D+PW zMNk81HU!Nl?cQ)*OK;-Ujy5$;H|c}5GHQwKu+`Dce${&?+IBfzpwr86$+ekOvKd(v z(<0G_f#waxq^cAn?#|X^M;;WDEA#++h#G3E{|Xn^aiI8<9NB`6IRgopOpk*_oB$u$ zje~-Jocg+IT;*|HM&SOF`V-+*W%~j+Yf6kUWg?0u2bIGxf$QqhD}S~jXxTGphy6ET zEio1HH;IHtb2AAtc5bjqT);kG&J$ii$q9Hufd-l6?igS7c6B+V*#7a}X4>`!k`cmk zKARcXVrVNoBpFKZx;DL*5W|Ti;*Z0Q@9IxV~I-Dyf#cmol%z^T%2_NMC~8tWL#sewE7;? z+NiO06Jg6s<+Hi)vSym)I$TYseY*G3z$VFk5QD;AnWkYV6(l z15=Ge*Vi=b%@6L#YUgt^A9@te+%q9^c}^`4?_~Pn>ijAKV|DjGQXG>Mv3G*O5WfzR z3AflnlgLI#+Efb^3C&$F6m`=T^&8OvgXNClh8g#$@+xz?%=8*L_NmkuwY&^3w@%p> zQ6|gTRO-{8B`9#90bd@4(mtWL5hHm6R#g0IKuvNTmMj*!O2Dg%g*sA-f5stfkwZap zlZO~fR{)|L(}j2ESeSNuh?W<0>nLRhxg&dFgVf+?ey?S zFt!*lVpp+RVJp}$&Z!SbZa9Z&tv2|D*5Ba; z#7BzN$Y!8t6J)D_FC0>!f9lNW)gE**XUfw+7k;^Shm9jT$*YrbZX;ci1?Fgajf(#R zn7X7%;t3+rzIepIhmyMIy*wVc#qm9TD=HQT&Jk%V2A(F1RN7;~bdKY()^3vZrR+6^Dh(XzyYR4igT!~kdZmL5n&w&y{H?`cI zWks~45l;PFA&mj{1VcA3jfBTgJsjD`BaBgWFDvzb>hpGCRse!Hq45^>|rrp zG-!tQSMnV^%Ag0!yC7MF;<2-V zuFhUD8YYFPMw|nMun1+td`z5@e$C&MUtGVV|5L`>t?_BezBLz z()pB*ZBiH=L-S=Y{i(BD8l^h{ypqn_Up0wOpj`STvC2>1=s-OIK#XZ+P{k75LmXCL z3vMzS=*SY*P?_1L z%gLbuObe4O_+KLCP1Sq+U}1n?QKR2b_1nKUV6y)??f*{!3l1(nfKw!wv1ThR^&h~j zt|XPtmj}3!eRuMPCNSvj?kk=#OR^318_t1~pTp>i#-a!RTvMvGA{(NP3a?D2qcWJJ z-coJIAX0OR-YC1luUDCk}CH>T=jHGFp` zwu5CJUgpG^N6FMP#uddJpu-~$ABGva3<-yV$0snZQ5;d(Ue*2D51VM<_Tqu%0{eda zjJ1L0%gTI4+ypg6P86}%XJUCz#mTvy-bH)QDTwHpD|wJSxIJ=v|HnvTXh*ie&4RbZ zHD5RNKL>5seaB-VFw7mEaZfP=E(Exx(D_TE!9Wrj$OOEQ?X0`_ z#YRBHo;QPF`qM6?c8(&*jBh_v9&+}IqArOkLb z?KJ8x-&U$M^I56<31E@PMv#y=QtXc@r15yEx+)#I)PiC>O^>*DLgp5&fEx>~;WwaH zNp0o`?!RG)73>~Gi2qTp9Ab<;*avO^F@9gnILi$-bU2O5#QzI>0FJRE*|Z=@)4S1w z7&yT-|ILLD;u*$5nW@Mbyx+m`g!kDz%~y=UR02%k90erZ>R077;E7 zYL6*}8c_^FhFKD=anE%L zW}9Ax_Qs~@z@^mKr?rtbw0;U>V|PtJx;HnIB6}W_xUV?M;yN4miYtmnEd)bK9 z=}%(6zcTEkg|no6xMV?uu*t+o)_)cy79(u%&20x4P7D>-Id_loOPMA44)ao#zj$&mYSf8Vj0jdj73)F{? zoWjE}i_z>ko8fIlap@paw_lSh}7(LvvYw9HV1)VG#6ZJ#OuOw420|j zOXMT-_7B&fak4OL=}wMq@>fJ1sP&z1jFDc1H3Xc`UV+O%wgBr}4w$v{g`rV~&CDOv z!_9PAL_DkT-1tt~QQ1W+quMz%uFGYk%CU$oF%$e^IOPhr2C*KwkNEnM(O2lALpP^ z!m?EfkpWP&dS4?SX`Y8|91ND98=M?s8h1)YoVN`5vRYkPct|0T)7hu|L;%MM%M)F` z;rF~eHE9(|bbW+i=#Pj5%i9#6ZZ|US>~fU4p2|BX96%%4H=3mny8ajnB6wR`9!jyC z6UA%Bjp2^0ND-O|8HOB|wRvNm-hN6iOMdcr!k=7zYqj6ayR`CVLeLH)y*B8?(2};k zuHq7wzR{no{st@8zKz8)KOXL_Ersq`Szc?i*Xu<8o=L0K(qqd7wYivd1L8&bQFtq! zsYF3HC%)++Q(PffCC)8J%Y7wy?ai22dxuy>ZmquaA!^-ouo9VwiD8tbkBf5c!poge zgW5ewB`OF6h#$pSzs19n1W-my@kp&xSlE?z=guI{XCwY+L@9I8Ns{|ei% zW|ZCn${GpsXh2j1`-p8}1I|d=|FNW{*7M_M|c~GujseUxBfaC~E_+$@RF~lnjKm zv`Wdl)u^?#A{%eV=4Nr)YaLoyWt;Pg-8Q@j3cp$@k})oJLtb`QGcz zmssaUDu3VtS)Yof|G4g08BO0%*o|BGfkEI#pd8Y_A{enVVo){Uc=DWndBSSkBjAN~ zf3k_T1i8e!r)4@WHjTx8t8aZ1Hm1o^I4#(0knHiE!csPq4lGnVBcef%u}nyEs2kiw zBMIM|1|9a8aEh5w=DVOPdecB~uL6>CzzNl*-P$;&h^ z#K>z<3{#@~i46YFy_D*BjR?*Z65Q}YM(ufZ@(qk2*}+M_J7Nws!GZ@BG^-}93)>q% zPTQOX7dLl27O+^c)o+8!AJbh>TC_B>;3TomUq+?$+dx145@K4xrKBi90$hMWW#uTols&Ex4a8qU9P2kiemIw+`jD$;8ATHSZbvj z@T^GM@W%Jr*0IOawPW&`HXQ}^q_d9;Oo&NZ$a>8d#%M2hK$?B1W?Jwu!|d(gMMH>x zXLhr+MQgQWqfrIjoCPiwczXSIcum$W)fe(u^FtO1I+Uo=yO?wSs5iP=_=Lmjxj3Fi zlPQ-)-x~T0UqcLoQ;$jgw97*YNpzUX-H30MvJ#z0>+UBY2jAwN7MeSC<@X7lKR*PW zNJiA?z_6=Yxkkvf8|L(rgO@t`m*oE)WlZiN#jOQUo+Wa)f8JUAC%dFz3}RILIQOT( zhu){F;p;;S#@>AMG_e#Bwg=j*3&EL}GzyPf`u57#Pu4oIYaXH3B`2R&vn`*W8hG4$ z*4umTmEBeieac>+F!9Y0eox7^jJh9|YZUfHCVlqXb@KLW{?QqB|4(TFKLCMsEzQlp z@dcVw8N?XP?S3?VP`5& zt0g~x;0B$;Y(C+;v`x02#2)~?bz4b?zFYRb)oN@&SQ4y3oKS3eSGz?gCtZIh`iknP z2zLU?18Z$3VB}vAOQOsWbAY$}Ks=MtR1ZZy=>hY6l5^0?gj)+$PLtgIFo_t6AA}nB z+V89&Nzlb!B8q(~hSPzyUdGOb;)qNLqGdZvsBsDOO;;o#mRXRh!@@=Yj1VMaX#&4Sx!9V9IpXa3 zz>roN$5T=%>X1{jilf0!IK=@wUUU*|&Rq_SwOQ^ZNXNs7Rn^|RpqaKIf+^hK_mcyC zl@8Ex!IX|Y0k7O=;`93M*0fv0kyNlg9A~@d^KXChe;G``oDryH=(&VGJD=6mbJavy zS<7n^dSE`p+ZH4Ba>rkl0G15WGfYAuPT=@3STypnpGLqg_ko+$DGD!Gpk$Uy8?8$W zj0lFPauUDI%7Q?UzUqwhW$IwLc|oa>Z9TNKAZ&6s9?Rj(PW0mN!LgZ3!A8@Ek5;%M zYSgXN0lw&)S`OCLRfgywaPY5!$Fr6GHPhV;p20a1oc>&X_qc6;D2x&S|xfQ7?$_9S-rBGo8# zmbjHps`775iRlYcB6WiKki}tHDve@OVOpPr9a@+$lbX(TY%?Acj{VWB&HQ&7{^aCk}Xs9>*-99z?n2jY1HCnY5pc;>t2DYDN@s}OOPPy2o6 zR9Lv{5^SOH5#U9IX|Rl&q8ghrCh!>%VT)7ZCJ->e2G)`Zfv3Acx?`@%%$y(mI_3K! zm^x<;zfbB7N0YT{LYfy7E63e|l1z!69j1+@7M@7j=bOdBIf4oIInNNOTAF0CW+#_n zUzsK3;$2QqJkeJYZcuPyD5xbn9T67Vo)w07`y+g6msUhSv}Mn(&FJw9`L;1;(Ox69 z1}@o&m&Ems83Rqsu>$M_I&V!F^2>2H3Ut%c1<*86xGE^5lOX*t--gS<1)fyexi~G`Gg|g4{>8TX*?!5EmLl%aggvmcn?h1!3Vl_{Qvv1h+IpNRF*M{8)P_Ub+R-k5>;f&bO zz6NvOrdRad(X46A806bmV78==4o<{4G2U>eGlv`GC(#mGGuH@plRXVUv>OOqFtMr{ z1_-^#%^P7-ghP!EoRkN@e zvwHw%wZcMEWfR3L2lsYM8u*Tc2Q@IW(DqKVC)f~{-0#qSinMWf{*-ZC;nRy z%V85g+Nl74Y-(;M@0{+L8Y7cn(AFoxgMn@A6-8N?abMh388nr6E$cl;QRdget z@ijOAr&}?`_w8qEr7#Cs>R!%bjE#){*J_mHVYF`H)LNK4tX?>`uWn^6R+Fm}&aqwq zoTbfGPu9gc@&>Ilf0xqpwF{>!xWEbVTMz>JGR$cgOe_vxU2JS@mNM*J7*SrLt@w?S z1Suss8zC=$c%xA>e&OSwkT;Fal#r84br&CrkWUY!?eweZ&WmD~rVkN=!5c$SX9`0I zWI@!lY)bNC2+tlGPAd0*34^?mQ~vBY>8 zb3&RWkkm;~?6Db!-fsozxdI0jdJ4?m8nq_mm&ZtLgqzO$WjgdF?NZZ-$kz7Mj9PLP zKOTsc;=k*JX^&Tw81IHnlrmBqpOpJ{0Z~TUtxgtOVuEg)PrQ%k5T(73P#V}LO? zzEE(niw;WA z51VU0HGU@&j&X(}Pmn~9xd27tj5OH$6U^1>?mHz*WPcQ%2Z>vic}WiUaW#K_&6Z`! z_8TX0+Lz5_$f#du<7JHqXjdndv)r%P^hvB4edxl6`Oj6|@K?Yb$vf9+cyVl$ur@#5 z`Y_4AYIaNs*Tr!-<);b6Iu$cSZ5h<}IZSKL1koXtf(8@@;2Yy7zgUoiJ~Q=!k>+|B zCX`;|OM}y3dr7|34D#kiy(l+Z#u;`5MaQjwKPv%6V#(|jd&hUz!I8&}$78*O_prq9 zDK_kjU1h{n?P9Z|bM(WeyTubYY)m|+Pay6|ktO2Xm5?j&1G{nG$<@fuVy?!uFNlui zW5W^mk4UfkIq7z)UACsw00U{m=l|i))1SrCe7g0D{w$?W;=~F zsFzwu{8@`K%MuRlkhG}gsSD!0i`Ft+gb6J34o_^)#yi`kn%aD?-j=9d1~RT*t#+~P zKnb0TM<`7R+lbdiugA>VaKUpe3{rqaElC`%oFO8gn0^p)YJmpAHat_OzE__m8AQ52 zX}$#E2lot>a`LTjDk&ZEX6^p+?KJ}`AA=`5!yp;lA-?3U(I5uh-r$9~WG*qB50a|nvGS(1 zDzOekTPG~(XTue28#KceEa{(b?mRo5RojMV?=RXP!2d*bJk$@#SK=U%-A!@ndO6@! zef3JW7_&L>NTN>!O+cBe&mOPhmiF(jzZ=&3zLDpU~2t`c+-%fvsv* zvH(`P7~?K%N$A`PMc14N0^b4#GOVsefecc}kW9%DqfSGT@A>(-a*rJ8P)LU%kTFDj zn53O?lQlYlSryIXh9pXmvHBp52kKWt(XW(gLq#HqRl~Qk z$1Z6#oKQrHK=FvtK8Tnw#`P>T*8miiMa)txXy3RThLKSP0yt6LW^d1no#m6TjD8xM zcF204S@-4#lnCKYGoSYnAGUbX-g@^w{^^ zeXRU;hN0iw@}Z+mCYukU8?q?;&T?f#aTnBR?Ogw%l@Vi(p2?LqK8DM*>g?4*jymZ5 zyvFl|-jVFC#hp_FHH)c9xZtRz;)IL~cCH-(%B0o0(XC-_TiT@!%4Dw@XRu=m4)J;Z zx8zWZ|EILC4vK4A_8ug7V8{d+28ZA}BzS7XPr%A5Yfn`<9i})3&ZDrZ4Y&M2vm%WlmXO zW5{)7IhTCPne$oH^QvK1#xS>M8SAm>G|?g5FpFGby2c>!*J3GY5MJaO^uz+WY|+XL z#5)@5SbjnF76t<1A!RtaVS#qrJ;|ma{_U;xDi5{Nz(<1}71$fu zu0j+)6s-ZYNiL6W!9(MV)ao z{~K^m*%BiC$DIWKAZ%gW-$`(PBPZ!2ENljTpd^T1{lQ~KHDf0}D8h(IK*}$x%`r+N zvLf>@nieJoh6F(R%y{MI#yKc~?#sEpci5cM6|Y-Y^US|2u1Tsc`P-m*we93(k0TqO zcpqFI4&MJsq$;yU2B4Ck(^+1(q-`HKtOs zhv|XNHp3M=Si6I~K6sjl_w!DP(OOzh&+>fD^wO>PwK0VWmjwN#2wF;0Ar z$V~QO&2K;=Lqw*Ipf>ijkTfyhHbz4M)Y!azU~-5R6%e8~NjcKiDoGJjt8@WQuidfC zOOPLAV*>Mee)zaw8$&BLv?bceEXve#M((TX!y~KyV`eq`U}d>$`dsNrU4J(;St5jb zu}Q(DEL`|1Kc#xYZwlYo6fs$B0!`GzGK-lqchUbbn|YR?+t7b2;aF*ZFxIAlaFLwW zd>`pf&^;1~9fFq-FDpsiXjq*r#R!He;R)WH))i%`;x{nb{hQ;kH=cekMJ- zw(y!I9Sq=NWw1Pyq08SXQHh%l7JTy%$8_QjNOz3)8xV}84&W#X)Q%eHnh|bQBa;G< zsTGj|oV6tv0pvmz$C`XaWKsp%1X)@ffolINH9J$TKSgZvplgT3t%%35OGfE8z>9x)B(SHNpJ1UP#$CawAb}ar&O10zSn(xQ|L|V>_FsUSp&i{|6H$U6L#~7)$ z^(Hcg5Y#`zTAF`pdE&IfNYW4Kqj5IF9--9x3Y>2!Tg_ltxs+{J+rT8eQrpB!8(ASv z>$9rB0EyjHG-bZi$dy(A3Tx!rs2=ekyttu&@hQg8(sC^@2e{8YYipOI)fImFQLZ(JW0K1OOi4RW5+j&ROSMps z+8E+KA`j_fjaM_EAkq zk1A`xptd)4YTv|)=gB}Vu_Nw|ukxb})h@O-WB2E(FX4U?aHS9Lnu{w?C(YqZGCcxE zi)+2%W}!P(#$doap0om-9ae@-!8g2Wup{{d>8qo}#OzR~V(eneX59&czSBE7b$3Z) zX2`YbUOY?E=L&bptQpaC9V9BgRc{}?|8$ag*6#rg2$fM&&(!5P8&`jL>LQLfN!FpC>POxZS>cGm;Iez3DE_=ZOUe+4Lfx!^!=4fEi_K(=Tb-0428 zZo9O)5v?hHFwsN@uT9SF=!dZA#-cZ8N0!4c~-2<@e988Pf9ULTBR2hFyY*1!x&! z&hP-~Z*ctGzVt_Rq8Xxrn@LFq&uP@EUuHz*h*e05af{7`y^c{)`o!#0y<2}-e$1b_ z(qj}O9@QON3v`_2N~Attx#~U-;7uENEB{bVIoZrBd!zZwF#e>=ebvk@vQKhOr$DrOJ%FQU=OT)^iJVtmVLPo#4v_$@k`+v-sM^fxMMwL_N$k6BPX5? zfgC}b9+|Zco`}f)U2HEPn9Qgc$RMxtw?#wZ%ybWBuo>l<_k1sjqQUGhRpy4oDCYWM zQ`Yz@R9eau-Elhp8^CpkzR&hP-3+-#H_dK_{ctJzOSvLURm3OKN4}ecM!vt;)au!P z2pn}C1#5_F}3X!6oA8t7^k0RM@lXA|QReV(P@*N(SrHpn6F3x)2q zGP-$+>Ss@`^z0{|=^cjK5jkLBG(BFEwiESyQ;{bk$2H5kb!gHKU3HTT+_A$*HjYJw z&fe5{R4t2e(tI8__1s!JB5jMBV0OVj|D0>mJ_t7z z4&F%|DC?PMLf~1a^NlqPEA8?CbfoOl?#R3gFZ6G+<}w2pE$B@c0kB(gWv1D4VY=Em zTh~(Wc*-Y(pWr!M_Pj<;z>0=a?0lG7=;p3$3kz1D|NGW!Ui|8Vp2%j@J0nViQ9>^t z)9!)b#oEVVApE)#=0pr4?1s?EOnGR==hU<@$C!n<;A8U0#P($j37Y4@G*EeZ|6ct4 zLWlzRl_Yz;`8b26mD$q|Zu>L>4!TzXN^*m%Wk%R-__?`((Lu7fZ(H$%-JQhp#cLS4 z6o_rZlsZ^o?t$Hb5zAmkkC*!LfCf#)JJx;>Rc8tuMU6CO-s0W6$j*xf(mJvpn698Ua5v=e8Zxs=+^u?E9$b}CQ1XlAn{~~FMGlHk zx6?mJ;Im+gYs@NgcLakmX2G%0<%m>rWkqo4u*E@;g=ArL#TM0#G*h_3I~@$>5Ca}Y zxz3*moX&?9a-Y^r^nGpjdd5?y%@`VMvoZa&qY?-JP(Zl)NSMOB2qgRfwRmZtb zMRnEeq&jmw!)T(8aL<%GAbzD~rw(T?(TW+rP(c!XPkFk3h46pxqams#ot0l7t3-#0 zOinLyXFUIf!s7IQkLz#{jFl&8L7ry|Bf>!1MOEne1nfAxr_MSQ9i*=Br}#wmI;!&W zZnMP2*^oZ|o>jPT{pu`%Kl4M03Kj}{5ei+nD{gfk#civ;z3cDpV%-SRNx(r(NE~3` z1`>$M$*XCR(LlN-0gOMy0(H`*{QCUJHnDsKB{T7`%%~u(bL2<{z+;lyKzkwMN%jm%>TaiSJ}cr|f?cj=06Y}M*VM^bcDt+R=O~=C_Ucua`^)N5*ge5T z-hSPv+C$G-!drcEBJZmf9{~4>)l5WPk7=>z?Kl7gm#`5*2Yn-%%|BvYPu5rF?RA<% z%$Pqsna$%5pZh`v*{J%}ofg}}%s|bEL3&S1;YTAlFrYcCjdZ7i3f({i=@@wiIjhJZ zq}~Pe8}JKszi$+|@Be^_f%#{f2W;HGIzM1w0!XnQF<{@9`GC~aAJg+`7&Afz5Jpa9 zevw&BCQ%h_KMow7|Jpg;_ll4J+)}}Al0h>rABNu!Zj09n6`#${SN)s0SeJ^5!h%EYt{oK5AAd(N1EOf>ygd4;)c z@c!m*b*>wck5x6)kBDDxx>WDnm@>lRN`-a*65=Y*)_ZhcGIFVO;QTSqT?DQPqt0MY zCsvIe#%1lZ#EqC>WBy2HlGh=}W+$CAU3`)}bbT6{G_J<4SGrsbri9QK_jDJc;!-ui zC83QOdicO-2gZDxagMS@vGBdaR(i2$sjXQ-3To>iRyRwp{S5$S z_FXDwG~VXP^0vMHpu#=n?n>}sBvp{Q?#MOwc8^*KWN6*)+WQ-@GDMx~{+cnOLrcfi zS?KGX3)43hh^AY8#=+r4J_BAZZWLL2*r|U@%GzSZT8W9o!_W~12%fN=BusSL-L~MF z1IWKUq5wZ8urqec_$m9FXVnH2j|$ZU1t;J>)07O;Je44ny4?<((7#yQtb2Mig3%U;2kWDu# zezuw&n2u82ABI%ACJoPhZ@zRxUu)62=io0z=&Fgc8A=mNHnPojF5zLCd@%JQ;kiBu zW~>Y>jq=_+T@9x1dci2hK1;0BYC^zxxvCI{m$s)>sFAC%em~+k{!;&=N%xeGCE$+H z`{fPdrs~4)(((}NXm|;CHKT*1Um}Dq-e{Rvcg!$zChMH@k>6KmpXxyr+Dp9tz%X2b z=i#NPD}0TMnFLsJg&$+^)k)^~R}1Aw(D#w#72_aE5&SI=SL)#Wcp>RMT;q9i)-pKZ zVQ_oNZC~6_u;`=U1S`WASzauGC&?6}|5W`D8*qX`Qz_BMPrX{{0~M0t8x&)+n1gue z7iCUb-_S8kY}l9i*s@FP zhjS7)kl|W7NsG&WEKXQGS26-MVWvty*Yg{-qN+xJ?hFab?H&d7a3`;Zk70o&omok% zCX!A1oKisKszh`8Fy&2vwklVqJn=I>cVwrrxo58pm z@0exdyd4ncrT5(6prv3c=lO|q%c4To&)+o;DTaxr8jx7I%es#4x@n&jh6-SLQ2hqP ziuiT#kGCbDSvvhe@Mq%<-o2q6F;T7&4O&aCc_c&xLfejYc9N>fcJYn@XuL&^39j|7$W!|GT!gJ7e?CBP zXqCOt^w0<|)MgvNM~_&tZb@u8u@a`V*PO($rI$PZ8Jqu6Kx!2lj`zQ zo~AE^ZHu>1{PgtZEk3(3d>Iy|6p`yASvv>+)scz0s|z(1o_Vp}=FNADGe=TagA6N~ zU6KR~{fS&~Z!k4~I(QOUwVqFmb#X*-~#=t357#NSfkO7yg}}8IpyAss8FJ8HFw?%&yzU zAp_Xjuo!hmMAgXPmzG(_fbFJbJ_6XyY)h-Abn{9s8EyCxL>y28X4)9^u4h-5OjqpE zGDN6{d$*r7k2G?Nqe^6!H$U0&5=XE~_&tHVeGqjI*<;!Obli*H)BE(Q0`nz*D$^FF zsgWK;uk4wY)tAj1Li8~tq)j7t{1jQ3JnqR$lECuc1 zd_by8lM<;bsg7@jsap)ezX90&9UE`)8p3-!0th(z-vvjghu1@pUrd(#zkg!;V?h69 zk9Qx{X_9bxF+#((cxk8@4&^7?gorM6(0tF><~wAVk>;k=7t!t5(#=eCr1q_NiVb7h zCftTPw((#16)XA6lFwyqZ;N<)5thM$_-Hm=Wz)v9uF zf6`Hk%epXI8uLfR1fGA3F2ny%V!NTG_WH`7T3>a5N+t2LT+9d(|FMFOyh&OSzdG-3 zfQ#H@-XtCST?!n)BUMgU(MJ{-7Wtp>M~RQ6KOk~WlJV5gO zNXFYP=a0Pqi_B-qBu-*3!U+QdmmCEsw=}I2dr~L7K8&Tq>S!Os~ zwlb{OY@t*XY}`a7$cPDeOwXD3sWL$J=(VsVdXI0Lj8c_^Ew^a`*ZAq}#~UK;r@DNxP!6xV+kT=-Z^ zO?wdWmi*S@Dn(cFDbt?kfE_qvyT>gH%6^cr>1fZRmw4zNVGzT>e3I3l2RbeI?hzG2 zDEo4sYZw2SOh1!VvDV~zVYAVsE|)%werz8?^K{doDx`BUQhH1CHvl=>%wU4hlaQV& Kx~pyZJ@sFVNY Date: Wed, 25 Dec 2019 22:38:42 +0200 Subject: [PATCH 104/227] Adding ts-check snippet --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2efcfd9..4e8fad2 100755 --- a/README.md +++ b/README.md @@ -87,9 +87,10 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * [Avoid using `eval()`](http://bonsaiden.github.io/JavaScript-Garden/#core.eval) * Pass functions instead of strings to setTimeout or setInterval, as this triggers eval() internally. * Use [JSHint](http://www.jshint.com/) / [JSLint](http://www.jslint.com/) - * [Inline lint highlighting for the Sublime Text 2 editor](https://github.com/SublimeLinter/SublimeLinter-for-ST2) - * [Interactive code linting framework for Sublime Text 3](https://github.com/SublimeLinter/SublimeLinter3) - * [Study and apply Software Principles such as **DRY**, **SOLID**, **KISS**, etc.](https://dev.to/pluralsight/my-5-favorite-software-design-principles-4ech) + * If you are using [VSCode](https://code.visualstudio.com/) you can add the following comment at the top of your code to enable type checking: `//@ts-check` ([See screenshot](https://github.com/kostasx/LearnJavascript#tscheck)) + * [Inline lint highlighting for the Sublime Text 2 editor](https://github.com/SublimeLinter/SublimeLinter-for-ST2) + * [Interactive code linting framework for Sublime Text 3](https://github.com/SublimeLinter/SublimeLinter3) +* [Study and apply Software Principles such as **DRY**, **SOLID**, **KISS**, etc.](https://dev.to/pluralsight/my-5-favorite-software-design-principles-4ech) *The difference between JavaScript and JavaScript with __use strict__* ![Alt text](/img/usestrict.jpg "Make a difference by coding with use strict") @@ -826,7 +827,11 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi

    3.11.3 ELM

    Elm

    - + +------ +Using Type Checking in VSCode +![Alt text](/img/ts-check.jpg "Type Checking in JavaScript") + ------ ![Alt text](/img/javascript-please-work.jpg "JavaScript please work") From da859295b365034d34f590532b6c64fbea1e39b2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 25 Dec 2019 22:40:22 +0200 Subject: [PATCH 105/227] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e8fad2..92b585b 100755 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * [Avoid using `eval()`](http://bonsaiden.github.io/JavaScript-Garden/#core.eval) * Pass functions instead of strings to setTimeout or setInterval, as this triggers eval() internally. * Use [JSHint](http://www.jshint.com/) / [JSLint](http://www.jslint.com/) - * If you are using [VSCode](https://code.visualstudio.com/) you can add the following comment at the top of your code to enable type checking: `//@ts-check` ([See screenshot](https://github.com/kostasx/LearnJavascript#tscheck)) + * If you are using [VSCode](https://code.visualstudio.com/) you can add the following comment at the top of your code to enable type checking: `//@ts-check` ([See screenshot](https://github.com/kostasx/LearnJavascript/blob/master/README.md#tscheck)) * [Inline lint highlighting for the Sublime Text 2 editor](https://github.com/SublimeLinter/SublimeLinter-for-ST2) * [Interactive code linting framework for Sublime Text 3](https://github.com/SublimeLinter/SublimeLinter3) * [Study and apply Software Principles such as **DRY**, **SOLID**, **KISS**, etc.](https://dev.to/pluralsight/my-5-favorite-software-design-principles-4ech) @@ -830,6 +830,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi ------ Using Type Checking in VSCode + ![Alt text](/img/ts-check.jpg "Type Checking in JavaScript") ------ From 36b68c9691ec4f809d54369188e0edd21a99315d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 4 Jan 2020 16:01:21 +0200 Subject: [PATCH 106/227] Adding excellent post on Prototypal Inheritance Visualized --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92b585b..4b362f7 100755 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) + * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. From 84ce5d7f1b308911dca0c79ec2a204eac279728e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 4 Jan 2020 16:02:12 +0200 Subject: [PATCH 107/227] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b362f7..fdd030f 100755 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * Read some books. You can find some great books for JS on the BOOKS section of this list. * Learn how to debug your JS code. Here are some good links as a starting point: - [Debugging JavaScript](https://developer.chrome.com/devtools/docs/javascript-debugging) - [Chrome DevTools Command Line API Reference](https://developer.chrome.com/devtools/docs/commandline-api) + * [Debugging JavaScript](https://developer.chrome.com/devtools/docs/javascript-debugging) + * [Chrome DevTools Command Line API Reference](https://developer.chrome.com/devtools/docs/commandline-api) * Study JavaScript Patterns and Anti-Patterns: From f474f8044f62376b7318896f006cc9735ced9727 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 4 Jan 2020 16:05:15 +0200 Subject: [PATCH 108/227] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fdd030f..bdd777d 100755 --- a/README.md +++ b/README.md @@ -144,6 +144,8 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi > APIs +* [**Web APIs**](https://developer.mozilla.org/en-US/docs/Web/API) +* [A presentation about several HTML5 APIs](http://eventloop.gr/frontend/html5/apis.html#/) (Contains useful links, code and resources in the slides) * [Screen Orientation API](http://www.sitepoint.com/introducing-screen-orientation-api/) #### 3.2 GENERAL From ef051307b915ab28eac4b6e07e49b85a8ce370b3 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 4 Jan 2020 16:24:05 +0200 Subject: [PATCH 109/227] What is JavaScript made of? --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bdd777d..f46b587 100755 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * [**Introduction to JavaScript** | Scrimba Interactive Screencasts | 69min ](https://scrimba.com/g/gintrotojavascript) * [**JavaScript First Steps** | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps) +* [**What Is JavaScript Made Of?**](https://overreacted.io/what-is-javascript-made-of/) * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) From 5c63a1ad394dbe6e542e14a2614c9642fade77c6 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 4 Jan 2020 16:28:13 +0200 Subject: [PATCH 110/227] How did I forget!? --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f46b587..424c8b1 100755 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi Here are 2 great videos on the subject by Paul Irish: * [10 Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=i_qE1iAmjFg) * [11 More Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=ARnp9Y8xgR4) +* Read (_or preferably buy_) every single book of the excellent [**You Don't Know JS**](https://github.com/getify/You-Dont-Know-JS) series by [Kyle simpson](https://twitter.com/getify) **[MUST READ]** * Watch every video by Douglas Crockford. * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) From 1e8fce9dce71308c8edfd04188dda83fb9c0d064 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 22 Jan 2020 22:58:30 +0200 Subject: [PATCH 111/227] Adding Just JavaScript by Dan Abramov and Maggie Appleton --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 424c8b1..08b5232 100755 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi #### 3.1 BEGINNER RESOURCES [ UPDATED ] * [**Introduction to JavaScript** | Scrimba Interactive Screencasts | 69min ](https://scrimba.com/g/gintrotojavascript) +* [**Just JavaScript** Course By Dan Abramov and Maggie Appleton](https://justjavascript.com/) * [**JavaScript First Steps** | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps) * [**What Is JavaScript Made Of?**](https://overreacted.io/what-is-javascript-made-of/) * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) @@ -591,6 +592,13 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi + Dan Abramov + Twitter + Blog + + + + David Flanagan Twitter Blog From afedecc9e17505175926f8a501746f1792fe6cc2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 26 Jan 2020 14:58:38 +0200 Subject: [PATCH 112/227] Adding (the awesome) Lin Clark --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 08b5232..ef62cb0 100755 --- a/README.md +++ b/README.md @@ -694,6 +694,14 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi Blog + + + Lin Clark + Twitter + Articles by Lin Clark at Mozilla Hacks + Medium + + Lucas Smith Twitter From c97ed86c0901e538b3665fb817bb5da3c8c42064 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 26 Jan 2020 14:59:26 +0200 Subject: [PATCH 113/227] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index ef62cb0..d803492 100755 --- a/README.md +++ b/README.md @@ -694,14 +694,12 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi Blog - Lin Clark Twitter Articles by Lin Clark at Mozilla Hacks Medium - Lucas Smith Twitter From 101fe452bbe1aeee42f51a2dde2870e328cdfd23 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 26 Jan 2020 15:01:06 +0200 Subject: [PATCH 114/227] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d803492..19e5735 100755 --- a/README.md +++ b/README.md @@ -697,8 +697,8 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi Lin Clark Twitter - Articles by Lin Clark at Mozilla Hacks - Medium + Articles at Mozilla Hacks / Medium + Lucas Smith From 12874ab41eca2885fef673e314ff763d82466ba4 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 27 Jan 2020 12:38:19 +0200 Subject: [PATCH 115/227] Adding resource for Scope --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19e5735..660b3db 100755 --- a/README.md +++ b/README.md @@ -11,9 +11,10 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) - Here are two really good posts explaining scope and hoisting: + Here are some really good resources explaining scope and hoisting: * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By [Lydia Hallie](https://github.com/lydiahallie) * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) + * [**Scope in JavaScript** - HTTP 203](https://www.youtube.com/watch?v=5LEuJNLfLN0) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) From 6f58e78df564375c914f744a42a2fb71e2c59fc4 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 27 Jan 2020 12:41:31 +0200 Subject: [PATCH 116/227] 27.01.2020 - Adding YouTube link for videos by Douglas Crockford --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 660b3db..422c3db 100755 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This list originated from this [discussion](http://www.linkedin.com/groupItem?vi * [10 Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=i_qE1iAmjFg) * [11 More Things I Learned from the jQuery Source](http://www.youtube.com/watch?v=ARnp9Y8xgR4) * Read (_or preferably buy_) every single book of the excellent [**You Don't Know JS**](https://github.com/getify/You-Dont-Know-JS) series by [Kyle simpson](https://twitter.com/getify) **[MUST READ]** -* Watch every video by Douglas Crockford. +* Watch every video by [Douglas Crockford](https://www.youtube.com/results?search_query=douglas+crockford). * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) * Study performance and code optimization From c05bd401b223daff0b78bd7cc7ea0dfa284aeb0e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 27 Jan 2020 12:42:49 +0200 Subject: [PATCH 117/227] 27.01.2020 - Updating description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 422c3db..13c5356 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ LearnJavascript: A list of resources ==================================== -This list originated from this [discussion](http://www.linkedin.com/groupItem?view=&gid=121615&item=5909742634301140992&type=member&commentID=5909826380652113920&trk=eml-ntf-hero-like-my-discussion-cmt&midToken=AQG2dgaKId8xLA&fromEmail=fromEmail&ut=2sQj9Dud2coCo1#commentID_5909826380652113920) on LinkedIn's Javascript group. +This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/groupItem?view=&gid=121615&item=5909742634301140992&type=member&commentID=5909826380652113920&trk=eml-ntf-hero-like-my-discussion-cmt&midToken=AQG2dgaKId8xLA&fromEmail=fromEmail&ut=2sQj9Dud2coCo1#commentID_5909826380652113920) on LinkedIn's Javascript group. ![Alt text](/img/When-in-Rome.jpg "When in Rome Image") *(Quote by the lovely [MPJ](https://twitter.com/mpjme?lang=en))* From d464fe4c33d054bba16c3940ccf9fe935172d7ac Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 27 Jan 2020 12:46:47 +0200 Subject: [PATCH 118/227] 27.01.2020 - Updating Backbone.JS section --- README.md | 59 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 13c5356..1aafdd4 100755 --- a/README.md +++ b/README.md @@ -326,31 +326,42 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [JavaScripting](http://www.javascripting.com/) * [Awesome JavaScript: A collection of awesome browser-side JavaScript libraries, resources and shiny things](https://github.com/sorrycc/awesome-javascript) -##### 3.4.1 BACKBONE.JS - -* [**Getting Started with Backbone.js**, By Miguel Mota](https://miguelmota.com/blog/getting-started-with-backbonejs/) -* [**Getting Started with Backbone**, by Koren Leslie Cohen](http://www.korenlc.com/backbone-js-tutorial-getting-started-with-backbone/) -* [**Awesome Backbone** List of Resources](https://github.com/instanceofpro/awesome-backbone/blob/master/README.md) -* [Introduction to Backbone Js and Setting Up an Working Environment – Learning Backbone Js](http://www.codebeerstartups.com/2012/12/introduction-to-backbone-js-and-setting-up-an-working-environment/) -* [Single Page ToDo Application With Backbone.js](http://code.tutsplus.com/tutorials/single-page-todo-application-with-backbonejs--cms-21417) -* [Developing Backbone.js Applications, *by Addy Osmani*](https://github.com/addyosmani/backbone-fundamentals) -* [Your First Backbone.js App – Service Chooser, *by Martin Angelov*](http://tutorialzine.com/2013/04/services-chooser-backbone-js/) -* [Sample application built with Backbone.js, Twitter Bootstrap, Node.js, Express, MongoDB, by *Christophe Coenraets*](https://github.com/ccoenraets/nodecellar) -* [Top 8 Common Backbone.js Developer Mistakes, By Mahmub Ridwan](http://www.toptal.com/backbone-js/top-8-common-backbone-js-developer-mistakes) - -> CODE - -* [Super Simple Backbone Starter Kit and Boilerplate](https://github.com/azat-co/super-simple-backbone-starter-kit) - -> BOOKS - -* [Developing Backbone.js Applications, *By Addy Osmani*](http://addyosmani.github.io/backbone-fundamentals/) - -> VIDEOS +
    + 3.4.1 BACKBONE.JS +

    + +
    +

    CODE

    +
    + +
    +

    BOOKS

    +
    + +
    +

    VIDEOS

    +
    + +
    -* [Backbone.js Video Tutorials on YouTube, by *Moshfegh Hamedani*](https://www.youtube.com/watch?v=4t0n5k0X7ow) -* [Backbone.js Tutorial for Beginners, by *Thomas Davis*](https://www.youtube.com/watch?v=FZSjvWtUxYk) -* [**Jeremy Ashkenas - Taking JavaScript Seriously with Backbone.js**](https://www.youtube.com/watch?v=4udR30JYenA)
    3.4.2 JQUERY From 17bef8934977aa6ac516b4f23045716d7794b10d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 30 Jan 2020 15:18:20 +0200 Subject: [PATCH 119/227] 30.01.2020 - Adding React Cheatsheet 2020 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1aafdd4..a536f36 100755 --- a/README.md +++ b/README.md @@ -419,6 +419,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g ##### 3.4.5 REACT.JS [ UPDATED ] * [**React Tutorial - Learn React - React Crash Course [2019]** YouTube video | 2h 25min ](https://www.youtube.com/watch?v=Ke90Tje7VS0) +* [**The React Cheatsheet for 2020** (+ real-world examples)](https://www.freecodecamp.org/news/the-react-cheatsheet-for-2020/) * [**Introduction to React** YouTube video by Le Wagon | 1h 53min](https://www.youtube.com/watch?v=_ZTT9kw3PIE) * [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) * [Getting Started With React](http://www.tysoncadenhead.com/blog/getting-started-with-react) From 886dc21edef85485a9832714f4e6516455d4fad6 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 30 Jan 2020 15:20:12 +0200 Subject: [PATCH 120/227] 30.01.2020 - Adding JustJavaScript newsletter by Dan Abramov --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a536f36..866867f 100755 --- a/README.md +++ b/README.md @@ -503,6 +503,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.8 NEWSLETTERS + * [**https://justjavascript.com/** By Dan Abramov](https://justjavascript.com/) * [JavaScript Weekly](http://javascriptweekly.com/) * [A Drip of JavaScript](http://designpepper.com/a-drip-of-javascript/) * [Node Weekly](http://nodeweekly.com/) From f6a150cd6795fa1abf3de1c2117c2630399494ae Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 3 Feb 2020 13:03:51 +0200 Subject: [PATCH 121/227] 03.02.2020 - Adding Asynchronous programming article --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 866867f..aa1925e 100755 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) * Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) + * [**Asynchronous Programming** (Excellend post)](https://eloquentjavascript.net/11_async.html) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. Here are 2 great videos on the subject by Paul Irish: From c9cd321b37a9ac68105856dbbe8466cfea94f63e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 12 Feb 2020 00:48:07 +0200 Subject: [PATCH 122/227] 12.02.2020 - Reviewed and added YouTube video about this keyword in JavaScript - Adding 'The cost of JavaScript in 2019' by Addy Osmani, in the Performance resources section. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index aa1925e..9faad8b 100755 --- a/README.md +++ b/README.md @@ -29,10 +29,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Watch every video by [Douglas Crockford](https://www.youtube.com/results?search_query=douglas+crockford). * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) + * [**Understanding Functions and 'this'** By Bryan Hughes](https://www.youtube.com/watch?v=AOSYY1_np_4) + * Study performance and code optimization Here is a good article by Nicholas Zakas: * [10 Javascript Performance Boosting Tips](http://jonraasch.com/blog/10-javascript-performance-boosting-tips-from-nicholas-zakas) + * [**The cost of JavaScript in 2019** By Addy Osmani](https://v8.dev/blog/cost-of-javascript-2019) * Check every tip on the [A Drip of JavaScript](http://designpepper.com/js-drip-archive/) list and subscribe to it. * Check [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/). From 9ebb44cacbe021685576e9256a7cc1b4ce51a27a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 25 Feb 2020 11:10:50 +0200 Subject: [PATCH 123/227] 25.02.2020 - Dmitry Soshnikov and his Evaluation Strategy article --- README.md | 168 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 9faad8b..6eac5b7 100755 --- a/README.md +++ b/README.md @@ -11,10 +11,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) * Get a good understaning of `Functions`, [`Scope` and `Hoisting`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) - Here are some really good resources explaining scope and hoisting: - * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By [Lydia Hallie](https://github.com/lydiahallie) - * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) - * [**Scope in JavaScript** - HTTP 203](https://www.youtube.com/watch?v=5LEuJNLfLN0) + - Understand exactly how non-primitive types are [passed to functions as arguments](http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/#call-by-value). + + - Here are some really good resources explaining scope and hoisting: + * [JavaScript Visualized: Hoisting](https://dev.to/lydiahallie/javascript-visualized-hoisting-478h), By [Lydia Hallie](https://github.com/lydiahallie) + * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) + * [**Scope in JavaScript** - HTTP 203](https://www.youtube.com/watch?v=5LEuJNLfLN0) * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) @@ -252,76 +254,109 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.3 NODE -> BEGINNERS +
    + BEGINNERS +
    + +
    -* [**Stream Adventure:** Go on an educational stream adventure!](https://github.com/substack/stream-adventure) -* [**test-anything:** Learn to test anything with TAP](https://github.com/finnp/test-anything) -* [Node.js for Beginnersby Maciej Sopyło](http://code.tutsplus.com/tutorials/node-js-for-beginners--net-26314) -* [Node School](http://nodeschool.io/) -* [Resources to Get You Up to Speed in Node.js](http://code.tutsplus.com/articles/resources-to-get-you-up-to-speed-in-nodejs--cms-21431) -* [Getting Started With NodeJS: Installing And Writing Your First Code](http://sub.watchmecode.net/getting-started-with-nodejs-installing-and-writing-your-first-code/) -* [UNDERSTANDING MODULE.EXPORTS AND EXPORTS IN NODE.JS](http://www.choskim.me/understanding-module-exports-and-exports-in-node-js/) -* [Introduction to the MEAN Stack](http://code.tutsplus.com/tutorials/introduction-to-the-mean-stack--cms-19918) -* [Felix's Node.js Beginners Guide](http://nodeguide.com/beginner.html) -* [8 NPM Tips for Better Node Development](http://scottksmith.com/blog/2014/06/25/8-npm-tips-for-better-node-development/) -* [Command-line utilities with Node.js](http://cruft.io/posts/node-command-line-utilities/) -* [Receiving Emails with Node](http://dailyjs.com/2014/06/05/mailin/) -* [Build a Killer Node.js Client for Your REST+JSON API](https://stormpath.com/blog/build-a-killer-node-dot-js-client-for-your-rest-plus-json-api/) -* [Beer Locker: Building a RESTful API With Node - Passport](http://scottksmith.com/blog/2014/05/29/beer-locker-building-a-restful-api-with-node-passport/) -* [Most Popular Node.js Frameworks for App Development](http://www.algoworks.com/blog/most-popular-node-js-frameworks-for-app-development/) -* [10 Tips to Make Your Node.js Web App Faster](http://www.sitepoint.com/10-tips-make-node-js-web-app-faster/) -* [LEARN ALL THE NODES: SCREENCASTS EXPLORING, EXPLAINING, AND EXPANDING THE WORLD OF NODE.JS](http://www.learnallthenodes.com/) - -> EXPRESS.JS - -* [Express.js Fundamentals](http://modernweb.com/2013/11/11/express-js-fundamentals/) -* [The Basics of Express Routes](http://modernweb.com/2014/04/07/the-basics-of-express-routes/) -* [LEARNING EXPRESS 4](http://blog.modulus.io/learning-express-four) -* [Creating a REST API using Node.js, Express, and MongoDB](http://coenraets.org/blog/2012/10/creating-a-rest-api-using-node-js-express-and-mongodb/) -* [Restful: A Better REST API Using Node.js With Express](http://benaugarten.com/blog/2013/01/31/restful-a-better-rest-api-using-node-dot-js-with-express/) -* [Using Express.js for APIs](http://strongloop.com/strongblog/using-express-js-for-apis/) -* [Build a Complete MVC Website With ExpressJS]() -* [Simple form handling with Express and Nodemailer](http://blog.ragingflame.co.za/2012/6/28/simple-form-handling-with-express-and-nodemailer) -* [Form Validation With ExpressJS](http://blogs.telerik.com/backendservices/posts/13-11-21/form-validation-with-expressjs) -* [Migrating Express.js 3.x to 4.x: Middleware, Route and Other Changes](http://webapplog.com/migrating-express-js-3-x-to-4-x-middleware-route-and-other-changes/) -* [Intro to Express.js: Parameters, Error Handling and Other Middleware](http://webapplog.com/intro-to-express-js-parameters-error-handling-and-other-middleware/) -> ADVANCED +
    + EXPRESS.JS +
    + +
    -* [Using Node.js in Production](http://modernweb.com/2014/06/23/using-node-js-in-production/) -* [Node.js in Production](http://blog.carbonfive.com/2014/06/02/node-js-in-production/) -* [Scaling Node.js with recluster](https://medium.com/@garychambers108/scaling-node-js-with-recluster-f04dd346108c) -* [Comparison: Tools to Automate Restarting Node.js Server After Code Changes](http://strongloop.com/strongblog/comparison-tools-to-automate-restarting-node-js-server-after-code-changes-forever-nodemon-nodesupervisor-nodedev/) -* [Error Handling in Node.js](http://www.joyent.com/developers/node/design/errors) -* [Using Node.js to join audio files](http://blog.ragingflame.co.za/2013/5/31/using-nodejs-to-join-audio-files) -> BOOKS +
    + ADVANCED +
    + +
    -* [Node: Up and Running, *by Tom Hughes-Croucher and Mike Wilson*](http://chimera.labs.oreilly.com/books/1234000001808/index.html) -* [The Node Beginner Book](http://www.nodebeginner.org/) -> VIDEOS +
    + BOOKS +
    + +
    -* [Express.js Tutorial: Build RESTful APIs with Node and Express | Mosh](https://www.youtube.com/watch?v=pKd0Rpw7O48) -* [Node.js Tutorial for Beginners: Learn Node in 1 Hour | Mosh](https://www.youtube.com/watch?v=TlB_eWDSMt4) -* [NodeTuts: Node.JS Video Tutorials](http://nodetuts.com/) -* [Node.JS videos @ egghead.io](https://egghead.io/technologies/node) -> TESTING CODE +
    + VIDEOS +
    + +
    -* [Runnable.com](runnable.com) -> HOSTING PLATFORMS +
    + TESTING CODE +
    + +
    -* [Heroku](https://www.heroku.com/) -* [Modulus](https://modulus.io/) -* [Nitrous.IO](https://www.nitrous.io/) -* [Cloud9](https://c9.io/) -* [OpenShift](https://www.openshift.com/) -* [NodeJitsu](https://www.nodejitsu.com/) -* [NodeGear](https://nodegear.com/) -* [AppFog](https://www.appfog.com/) -* [Codio.io](https://codio.com/) +
    + HOSTING PLATFORMS +
    + +
    #### 3.4 FRAMEWORKS AND LIBRARIES @@ -653,6 +688,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + + Dmitry Soshnikov + Twitter + Blog + YouTube + + Dustin Diaz Twitter Blog From 84094d3378ed765205fa57fbf3504caba4a9fcf3 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 25 Feb 2020 11:19:56 +0200 Subject: [PATCH 124/227] 25.02.2020 - Updating Node.JS Section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - THE BEGINNER’S GUIDE: Understanding Node.js & Express.js fundamentals, by Linda Vivah - Practical Mini-Projects in Node.js - Umar Hansa - Deploying Node.js App With PM2 - Alexandre Strzelewicz: Production Applications with PM2 - JSConf.Asia 2015 --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6eac5b7..4b13194 100755 --- a/README.md +++ b/README.md @@ -255,9 +255,10 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.3 NODE
    - BEGINNERS + BEGINNERS (Updated)
    +
    TESTING CODE @@ -355,6 +360,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    + +
    HOSTING PLATFORMS
    @@ -371,12 +378,29 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    -#### 3.4 FRAMEWORKS AND LIBRARIES + + +
    + CERTIFICATES +
    +
    +
    + + -> LISTS +

    3.4 FRAMEWORKS AND LIBRARIES

    -* [JavaScripting](http://www.javascripting.com/) -* [Awesome JavaScript: A collection of awesome browser-side JavaScript libraries, resources and shiny things](https://github.com/sorrycc/awesome-javascript) +
    +

    LISTS

    +
    + +
    3.4.1 BACKBONE.JS @@ -420,39 +444,67 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

    A tidy repository of jQuery plugins, by @psnka

    + -##### 3.4.3 ANGULAR & ANGULAR JS [ UPDATED ] +
    3.4.3 ANGULAR & ANGULAR JS [ UPDATED ]
    -> TUTORIALS: ANGULAR +
    + ANGULAR +
    +
    +

    TUTORIALS: ANGULAR

    +
    + + +
    -* [**Angular Tutorial for Beginners: Learn Angular from Scratch** | Mosh | YouTube video | 2h](https://www.youtube.com/watch?v=k5E2AVpwsko) + -> TUTORIALS: ANGULARJS +
    + ANGULAR.JS +
    +
    +

    TUTORIALS: ANGULARJS

    +
    + -* [3 Reasons to Choose AngularJS for Your Next Project](http://code.tutsplus.com/tutorials/3-reasons-to-choose-angularjs-for-your-next-project--net-28457) -* [Comprehensive Beginner’s Guide to AngularJS](http://antjanus.com/blog/web-development-tutorials/front-end-development/comprehensive-beginner-guide-angularjs/) -* [Recipes with Angular.js](http://fdietz.github.io/recipes-with-angular-js/) -* [AngularJS for Absolute Beginners, *by David East*](http://medialoot.com/blog/angularjs-for-absolute-beginners/) -* [Learn AngularJS With These 5 Practical Examples](http://tutorialzine.com/2013/08/learn-angularjs-5-examples/) -* [How to Learn AngularJS - Your AngularJS Sherpa](http://www.ng-newsletter.com/posts/how-to-learn-angular.html) -* [ANGULARJS: SHOPPING LIST APPLICATION](http://return-true.com/2013/06/angularjs-shopping-list-application-part-1/) -* [Building a Spreadsheet in 20 Minutes with Angular.js, *by David Graunke*](http://thomasstreet.com/blog/legacy/spreadsheet.html) -* [Learning AngularJS by Example – The Customer Manager Application](http://weblogs.asp.net/dwahlin/learning-angularjs-by-example-the-customer-manager-application) -* [Web Spreadsheet in 99 lines using Angular](https://github.com/audreyt/500lines/blob/master/spreadsheet/chapter.md) -* [Fun with AngularJS!](http://devgirl.org/2013/03/21/fun-with-angularjs/) -* [AngularJS Cheat Sheet](http://www.cheatography.com/proloser/cheat-sheets/angularjs/) -* [Speeding up AngularJS apps with simple optimizations](http://www.binpress.com/tutorial/speeding-up-angular-js-with-simple-optimizations/135) -* [Optimizing AngularJS: 1200ms to 35ms](http://blog.scalyr.com/2013/10/angularjs-1200ms-to-35ms/) +
    +

    VIDEOS: ANGULARJS

    +
    -> VIDEOS: ANGULARJS + -* [AngularJS Fundamentals In 60-ish Minutes, *by Dan Wahlin*](https://www.youtube.com/watch?v=i9MHigUZKEM) -* [AngularJS videos @ egghead.io](https://egghead.io/technologies/angularjs) -* [Introduction to Angular.js in 50 Examples, *by Curran Kelleher*](https://www.youtube.com/watch?v=TRrL5j3MIvo) +
    +

    NEWSLETTERS

    +
    -> NEWSLETTERS + +
    -* [ng-newsletter: The free, weekly newsletter of the best AngularJS content on the web](http://www.ng-newsletter.com/) +
    From d29a901fa8dfec41c264f5fa144b5984c08fe2e9 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Mar 2020 17:57:37 +0200 Subject: [PATCH 133/227] 21.03.2020 - Adding Svelte.JS --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 08e309f..08cc0be 100755 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [CascadiaJS](https://www.youtube.com/user/cascadiajs) * [JSConf](https://www.youtube.com/user/jsconfeu/videos) -* Learn some framework or library like [jQuery](http://jquery.com/), [React](https://reactjs.org/), [Angular](https://angular.io/), [Vue.JS](https://vuejs.org/), [Backbone.JS](http://backbonejs.org/), [underscore](http://underscorejs.org/), [AngularJS](https://angularjs.org/), [Ember](http://emberjs.com/), [Knockout.JS](http://knockoutjs.com/), etc. +* Learn some framework or library like [jQuery](http://jquery.com/), [React](https://reactjs.org/), [Angular](https://angular.io/), [Vue.JS](https://vuejs.org/), [SVELTE](https://svelte.dev/), [Backbone.JS](http://backbonejs.org/), [underscore](http://underscorejs.org/), [AngularJS](https://angularjs.org/), [Ember](http://emberjs.com/), [Knockout.JS](http://knockoutjs.com/), etc. * [Try building a ToDo App using one of these libraries](http://todomvc.com/) * Get to know [Node.JS](http://nodejs.org/) and start building apps. * Try running server-side JS code usign `Node.JS` on [Runnable.com](http://runnable.com/) @@ -458,7 +458,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    +
    @@ -502,7 +502,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    +
    From 86c4d4ad8585b3650a2c687c00818a85cfd1da5f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Mar 2020 17:58:54 +0200 Subject: [PATCH 134/227] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 08cc0be..56ee6d6 100755 --- a/README.md +++ b/README.md @@ -454,7 +454,6 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

    TUTORIALS: ANGULAR

    - @@ -484,21 +483,17 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
  • Speeding up AngularJS apps with simple optimizations
  • Optimizing AngularJS: 1200ms to 35ms
  • -

    VIDEOS: ANGULARJS

    - -

    NEWSLETTERS

    - From 8265e7a787216baf5859f58e415432ca09edd081 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 21 Mar 2020 18:02:56 +0200 Subject: [PATCH 135/227] 21.03.2020 - Adding //@ts-check tip --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 56ee6d6..3ac8df9 100755 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Always use `let` or `const` when declaring variables. Avoid using `var`. * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) +* If you are using [VSCode Editor](https://code.visualstudio.com/) add the `//@ts-check` comment at the top of your code for an extra layer of checks via [TypeScript Linter](https://raw.githubusercontent.com/kostasx/LearnJavascript/master/img/ts-check.jpg) * [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonely Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) * [Prefer using `===` over `==`](http://bonsaiden.github.io/JavaScript-Garden/#types.equality) * Always use curly braces From c78616fe9c060a09f55745d68715b8650cbd59a0 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 23 Mar 2020 13:28:32 +0200 Subject: [PATCH 136/227] 23.03.2020 - Adding 'Dive Into Modern Web Development course' --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ac8df9..8791a2e 100755 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g --- + + ### LEARNING * Understand [type coercion](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch2.md) @@ -150,8 +152,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**How to Become a Great JavaScript Developer***](http://blog.ustunozgur.com/javascript/programming/books/videos/2015/06/17/how_to_be_a_great_javascript_software_developer.html) * [FAQ for comp.lang.javascript](http://pointedears.de/scripts/faq/cljs/) -> COURSES +> COURSES [ UPDATED ] +* [**Deep Dive Into Modern Web Development** | Full Stack course by The University of Helsinki ](https://fullstackopen.com/en/) * [CodeCombat: Learn to Code by Playing a Game](http://codecombat.com/) * [Javascript for Beginners, Udemy **PAID COURSE**](https://www.udemy.com/beginning-javascript/) From 16d6910c4369ec3dbcd724f8f07b687a0000b860 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 9 Apr 2020 18:15:55 +0300 Subject: [PATCH 137/227] 09.04.2020 - Adding "React in a nutshell" video from Mosh Hamedani --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8791a2e..dc60f84 100755 --- a/README.md +++ b/README.md @@ -522,6 +522,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g ##### 3.4.5 REACT.JS [ UPDATED ] * [**React Tutorial - Learn React - React Crash Course [2019]** YouTube video | 2h 25min ](https://www.youtube.com/watch?v=Ke90Tje7VS0) + * [**Quick React.JS Overview** (Part of the video above)](https://www.youtube.com/embed/Ke90Tje7VS0/?start=75&end=286) * [**The React Cheatsheet for 2020** (+ real-world examples)](https://www.freecodecamp.org/news/the-react-cheatsheet-for-2020/) * [**Introduction to React** YouTube video by Le Wagon | 1h 53min](https://www.youtube.com/watch?v=_ZTT9kw3PIE) * [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) From f421aa4c82d582a3f49846452a16d4daaefd70ac Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 11 Apr 2020 01:56:58 +0300 Subject: [PATCH 138/227] 11.04.2020 - Adding 'Learn jQuery in 15 minutes' --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc60f84..0a2901b 100755 --- a/README.md +++ b/README.md @@ -445,7 +445,11 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    3.4.2 JQUERY -

    A tidy repository of jQuery plugins, by @psnka

    +
    +
    From 0955b4bd065416baf383a343f8463e29d168c8ed Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 11 Apr 2020 01:59:38 +0300 Subject: [PATCH 139/227] Update README.md --- README.md | 79 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 0a2901b..6c368bf 100755 --- a/README.md +++ b/README.md @@ -523,40 +523,51 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

    Learn Knockout.JS

    -##### 3.4.5 REACT.JS [ UPDATED ] - -* [**React Tutorial - Learn React - React Crash Course [2019]** YouTube video | 2h 25min ](https://www.youtube.com/watch?v=Ke90Tje7VS0) - * [**Quick React.JS Overview** (Part of the video above)](https://www.youtube.com/embed/Ke90Tje7VS0/?start=75&end=286) -* [**The React Cheatsheet for 2020** (+ real-world examples)](https://www.freecodecamp.org/news/the-react-cheatsheet-for-2020/) -* [**Introduction to React** YouTube video by Le Wagon | 1h 53min](https://www.youtube.com/watch?v=_ZTT9kw3PIE) -* [Learn React in 48 interactive screencasts](https://scrimba.com/g/glearnreact) -* [Getting Started With React](http://www.tysoncadenhead.com/blog/getting-started-with-react) -* [**Started with React** - An Overview and Walkthrough](https://www.taniarascia.com/getting-started-with-react/) -* [Thinking in React](https://reactjs.org/docs/thinking-in-react.html) -* [(YouTube) Learn React - React Crash Course [2019] - React Tutorial with Examples | Mosh](https://www.youtube.com/watch?v=Ke90Tje7VS0) -* [**Understand State vs Props by relating it to Plain JS functions**](https://stackoverflow.com/questions/27991366/what-is-the-difference-between-state-and-props-in-react#answer-50229738) -* [How to Learn React — A roadmap from beginner to advanced (2018)](https://www.freecodecamp.org/news/learning-react-roadmap-from-scratch-to-advanced-bff7735531b6/) -* [React 101 - Learn how to build 3 practical React components from scratch!](https://ihatetomatoes.net/get-react-101/) -* [Learn React for free](https://scrimba.com/g/glearnreact) -* [Learn React in 10 tweets (with hooks)](https://twitter.com/chrisachard/status/1175022111758442497) -* [Introduction to React by Le Wagon [Video]](https://www.youtube.com/watch?v=_ZTT9kw3PIE) -* [React.JS @ Reddit](https://www.reddit.com/r/reactjs) -* [**React Component Patterns by Michael Chan**](https://www.youtube.com/watch?v=YaZg8wg39QQ) -* [What I wish I knew when I started to work with React.js](https://medium.freecodecamp.org/what-i-wish-i-knew-when-i-started-to-work-with-react-js-3ba36107fd13) -* [Create React App: How to Update to New Versions?](https://create-react-app.dev/docs/updating-to-new-releases/) -* [React Routing: Quick Start](https://reacttraining.com/react-router/web/guides/quick-start) -* [React Routing: Difference between exact path and route path](https://stackoverflow.com/questions/49162311/react-difference-between-route-exact-path-and-route-path) -* [React Routing: A Bluffer's Information to React Router V4 – FreeCodeCamp](https://epeak.info/2019/03/26/a-bluffers-information-to-react-router-v4-freecodecamp-org/) -* [React Hooks: Thinking in React Hooks](https://wattenberger.com/blog/react-hooks) -* [State Management: Using the native Context API (React 16.3+)](https://reactjs.org/docs/context.html) -* [State Management: What is the Context API? (NetNinja Video)](https://www.youtube.com/watch?v=XkBB3pPY3t8) -* [State Management: Introducing the React Context API - YouTube](https://www.youtube.com/watch?v=yzQ_XulhQFw) -* [State Management: Simple example using Context API](https://codesandbox.io/s/reactjs-context-api-peflc) -* [State Management: The PubSub Pattern](http://robertmarkbramprogrammer.blogspot.com/2019/02/using-pubsubjs-in-react.html) -* [State Management: The PubSub Pattern: Demo](https://codesandbox.io/s/reactjs-pubsub-fjwdj) -* [State Management: Using Flux for unidirectional data flow](https://facebook.github.io/flux/) -* [State Management: Redux](https://react-redux.js.org/) -* [State Management: MobX](https://mobx.js.org/) + + +
    + + 3.4.6 REACT.JS [ UPDATED ] + +
    + +
    #### 3.5 BOOKS From 371c9f03dbba0d9cbd3fbd1911613103354d08a8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 11 Apr 2020 02:04:30 +0300 Subject: [PATCH 140/227] 11.04.2020 - Adding npm scripts link and reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c368bf..21a7543 100755 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Try running server-side JS code usign `Node.JS` on [Runnable.com](http://runnable.com/) * Install [Node.JS](http://nodejs.org/) locally and start experimenting. * Subscribe to Newsletters and stay up to date with JS and Node. *(See section `3.8 NEWSLETTERS`)* -* Become familiar with a Task Manager like [Grunt](http://gruntjs.com/) or [Gulp](#gulp). +* Become familiar with a Task Manager like [Grunt](http://gruntjs.com/) or [Gulp](#gulp) and/or learn how to use [npm scripts](https://www.freecodecamp.org/news/introduction-to-npm-scripts-1dbb2ae01633/). * Start building cross-platform Desktop Applications using HTML/CSS/JavaScript in [Electron.JS](https://electronjs.org/) or [Node-Webkit](https://nwjs.io/). * Follow some of the masters on Twitter. *(See `section 3.6` of the RESOURCES section)* * Code using a [Style Guide](https://github.com/airbnb/javascript). From f0a5b1eafef87e3e4ca5eb7bb1d3afe16c94228f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 11 Apr 2020 03:07:50 +0300 Subject: [PATCH 141/227] 11.04.2020 - Adding 'ReactJS Tutorial for Beginners' --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 21a7543..6109676 100755 --- a/README.md +++ b/README.md @@ -536,6 +536,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
  • Quick React.JS Overview (Part of the video above)
  • +
  • + ReactJS Tutorial for Beginners [YouTube Playlist] +
  • The React Cheatsheet for 2020 (+ real-world examples)
  • Introduction to React YouTube video by Le Wagon | 1h 53min
  • Learn React in 48 interactive screencasts
  • From fe2d9a6e84cf92d15ee9516981f23d551eb6c16c Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 11 Apr 2020 03:10:01 +0300 Subject: [PATCH 142/227] 11.04.2020 - Adding npm scripts --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6109676..56838fd 100755 --- a/README.md +++ b/README.md @@ -947,6 +947,10 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g ##### 3.10.2 BUILD TOOLS / TASK RUNNERS +###### npm + +* [Introduction to NPM Scripts](https://www.freecodecamp.org/news/introduction-to-npm-scripts-1dbb2ae01633/) + ###### GRUNT * [An Introduction To Grunt](http://code.tutsplus.com/articles/an-introduction-to-grunt--wp-34728) From b73f2e0bc272ac0529eb718e726a65e1a33f3538 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:40:52 +0300 Subject: [PATCH 143/227] 17.04.2020 - Adding resources on Async/Callbacks/IIFEs --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56838fd..242e67c 100755 --- a/README.md +++ b/README.md @@ -29,8 +29,18 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) * [**Marcus Phillips: Secrets and Lies about JavaScript classes** (Low-quality video but definitely worth watching)](https://www.youtube.com/watch?v=_JJgSbuj5VI) -* Understand `callbacks`, `IIFEs` and [`asynchronicity`](https://github.com/kostasx/LearnJavascript#async) - * [**Asynchronous Programming** (Excellend post)](https://eloquentjavascript.net/11_async.html) +* Make sure to understand **callbacks**, **IIFEs** and [**asynchronicity**](https://github.com/kostasx/LearnJavascript#async) as they are some of the most commonly encountered and crucial concepts in JS + * Callbacks in JavaScript: + * [Callbacks in JavaScript](https://zellwk.com/blog/callbacks/) + * [Callback function (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) + * [JavaScript: What the heck is a Callback?](https://codeburst.io/javascript-what-the-heck-is-a-callback-aba4da2deced) + * Immediately Invoked Function Expression (IIFEs): + * [IIFE (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) + * [Immediately Invoked Function Expression - Beau teaches JavaScript](https://www.youtube.com/watch?v=3cbiZV4H22c) + * [The IIFE Pattern (Video by Kyle Simpson)](https://frontendmasters.com/courses/javascript-foundations/iife-pattern/) + * Asynchronous Programming in JavaScript: + * [**Asynchronous Programming** (Excellent post)](https://eloquentjavascript.net/11_async.html) + * [**JavaScript Visualized: Promises & Async/Await** (Excellent post)](https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke) * Study the source code of popular libraries: [jQuery](http://jquery.com/), [underscore](http://underscorejs.org/), etc. Here are 2 great videos on the subject by Paul Irish: From 88d076aea01e4fee3e3aa8c232785cce61dc06c2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:43:18 +0300 Subject: [PATCH 144/227] Update README.md --- README.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 242e67c..8448d94 100755 --- a/README.md +++ b/README.md @@ -582,15 +582,23 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    -#### 3.5 BOOKS - - * [**YDKJS: You-Dont-Know-JS**, A book series on JavaScript by Kyle Simpson](https://github.com/getify/You-Dont-Know-JS) - * [JavaScript: The Good Parts, *by Douglas Crockford*](http://shop.oreilly.com/product/9780596517748.do) - * [JavaScript, The Definitive Guide, *by David Flanagan*](http://shop.oreilly.com/product/9780596805531.do) - * [High Performance JavaScript, *by Nicolas Zakas*](http://shop.oreilly.com/product/9780596802806.do) - * [Secrets of the JavaScript Ninja, *by John Resig*](http://ejohn.org/blog/secrets-of-the-javascript-ninja-released/) - * [Programming JavaScript Applications, *by Eric Elliot*](http://learn-javascript.ericelliott.me/programming-javascript-applications/) - * [Professional JavaScript for Web Developers *by Nicolas Zakas*](http://www.wrox.com/WileyCDA/WroxTitle/Professional-JavaScript-for-Web-Developers-3rd-Edition.productCd-1118222199.html) + + +
    + + 3.5 BOOKS + +
    + +
    #### 3.6 ONLINE BOOKS From e8582c3f368ef3ec14cbf8e92c3de2e26bf7df8f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:45:06 +0300 Subject: [PATCH 145/227] Update README.md --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8448d94..983afb6 100755 --- a/README.md +++ b/README.md @@ -600,16 +600,24 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    -#### 3.6 ONLINE BOOKS - - * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/javascript-for-cats.pdf) **(Free PDF)** - * [Eloquent Javascript, *by Marijn Haverbeke*](http://eloquentjavascript.net/) **(Free Online Version)** - * [Speaking JavaScript: An In-Depth Guide for Programmers, by *Dr. Axel Rauschmayer*](http://speakingjs.com/) **(Free Online Version)** - * [JavaScript Essentials](http://www.techotopia.com/index.php/JavaScript_Essentials) - * [Oh My JS: The Best JavaScript Articles, *by Azat Mardan*](https://leanpub.com/ohmyjs) - * [Understanding ECMAScript 6, *by Nicholas Zakas*](https://leanpub.com/understandinges6/read) - * [JavaScript Enlightenment](http://www.javascriptenlightenment.com/) - * [You Don't Know JS: A JavaScript book series](https://github.com/getify/You-Dont-Know-JS) + + +
    + + 3.6 ONLINE BOOKS + +
    + +
    #### 3.7 VIDEO & AUDIO From 746cd9789aeecb7b0d2d054491701775fe28e116 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:46:47 +0300 Subject: [PATCH 146/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 983afb6..13c9c0a 100755 --- a/README.md +++ b/README.md @@ -615,7 +615,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
  • Oh My JS: The Best JavaScript Articles, by Azat Mardan
  • Understanding ECMAScript 6, by Nicholas Zakas
  • JavaScript Enlightenment
  • -
  • You Don't Know JS: A JavaScript book series
  • +
  • You Don't Know JS: A JavaScript book series[ HIGHLY RECOMMENDED ]
  • From a7289f488ccfe6a5b51e4329885a170938669b53 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:49:26 +0300 Subject: [PATCH 147/227] Update README.md --- README.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 13c9c0a..f914771 100755 --- a/README.md +++ b/README.md @@ -651,13 +651,25 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [The Origin of Javascript with Brendan Eich](http://javascriptjabber.com/124-jsj-the-origin-of-javascript-with-brendan-eich/) * [5 Minutes of JavaScript](http://five-js.envylabs.com/) -#### 3.8 NEWSLETTERS - * [**https://justjavascript.com/** By Dan Abramov](https://justjavascript.com/) - * [JavaScript Weekly](http://javascriptweekly.com/) - * [A Drip of JavaScript](http://designpepper.com/a-drip-of-javascript/) - * [Node Weekly](http://nodeweekly.com/) - * [ng-newsletter: The free, weekly newsletter of the best AngularJS content on the web](http://www.ng-newsletter.com/) + + +
    + + 3.6 ONLINE BOOKS + +
    + +
    + + +
    3.9 DEVELOPERS (TWITTER, BLOGS) From 265243e90330fe98e40432cc7b44873ba5143dfc Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 17 Apr 2020 13:50:52 +0300 Subject: [PATCH 148/227] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f914771..fa24c50 100755 --- a/README.md +++ b/README.md @@ -652,15 +652,15 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [5 Minutes of JavaScript](http://five-js.envylabs.com/) - +
    - 3.6 ONLINE BOOKS + 3.8 NEWSLETTERS
      -
    • https://justjavascript.com/ By Dan Abramov
    • +
    • https://justjavascript.com/ By Dan Abramov [ HIGHLY RECOMMENDED ]
    • JavaScript Weekly
    • A Drip of JavaScript
    • Node Weekly
    • From 74a504eadd8a9fca9b38b6e3c399894549c3f6e9 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 30 Apr 2020 01:54:55 +0300 Subject: [PATCH 149/227] 30.40.2020 - Adding ES6 Resources --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index fa24c50..2e1ea6d 100755 --- a/README.md +++ b/README.md @@ -196,6 +196,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [JavaScript and the Browser: Under the Hood, *by Ariya Hidayat*](https://speakerdeck.com/ariya/javascript-and-the-browser-under-the-hood) * [JSdo It: Share JavaScript, HTML5 and CSS.](http://jsdo.it/) +> ES6 (EcmaScript 2015) + +* [A tutorial to JavaScript Arrow Functions **(Flavio Copes)**](https://flaviocopes.com/javascript-arrow-functions/) +* [Arrow functions, the basics **(JavaScript.info)**](https://javascript.info/arrow-functions-basics) +* [Arrow functions revisited **(JavaScript.info)**](https://javascript.info/arrow-functions) + > PERFORMANCE * [JavaScript Performance Analysis: Keeping the Big Picture, *by Ariya Hidayat*](http://calendar.perfplanet.com/2013/javascript-performance-big-picture/) From 8228a11e0b0ff8384664b24727d57daf2f10d12d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 1 May 2020 23:55:47 +0300 Subject: [PATCH 150/227] 01.05.2020 - Adding Module Bundlers --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 2e1ea6d..1cf573d 100755 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Install [Node.JS](http://nodejs.org/) locally and start experimenting. * Subscribe to Newsletters and stay up to date with JS and Node. *(See section `3.8 NEWSLETTERS`)* * Become familiar with a Task Manager like [Grunt](http://gruntjs.com/) or [Gulp](#gulp) and/or learn how to use [npm scripts](https://www.freecodecamp.org/news/introduction-to-npm-scripts-1dbb2ae01633/). +* Learn how to use Module Bundlers like [WebPack](https://webpack.js.org/), [Parcel](https://parceljs.org/), [Browserify](http://browserify.org/) and [other](#build_tools). * Start building cross-platform Desktop Applications using HTML/CSS/JavaScript in [Electron.JS](https://electronjs.org/) or [Node-Webkit](https://nwjs.io/). * Follow some of the masters on Twitter. *(See `section 3.6` of the RESOURCES section)* * Code using a [Style Guide](https://github.com/airbnb/javascript). @@ -223,6 +224,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**The Evolution of Asynchronous JavaScript**](https://blog.risingstack.com/asynchronous-javascript/) * [**Javascript Async Control Flow**, By KENNY KAYE | 20 OCTOBER 2015](https://kaye.us/javascript-async-control-flow/) + + +
      DEBUGGING @@ -231,6 +235,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    + + +
    LINTERS @@ -241,6 +248,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    + +
    REGULAR EXPRESSIONS [ UPDATED ]Regular Expressions in JavaScript - #1 REGEX ULTRA BASICS | YouTube Video | 23min

    @@ -382,7 +391,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    +
    HOSTING PLATFORMS
      @@ -400,7 +409,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
      +
      CERTIFICATES
        @@ -458,6 +467,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
      +
      3.4.2 JQUERY @@ -532,6 +542,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

      Ember.js introduction by Kasper Tidemann [VIDEO]

      + +
      3.4.5 KNOCKOUT JS @@ -539,6 +551,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

      Learn Knockout.JS

      +
      @@ -989,21 +1002,40 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [Introduction to GitHub, youTube video *by Curran Kelleher*](https://www.youtube.com/watch?v=Q6HbQRWAMM4) -##### 3.10.2 BUILD TOOLS / TASK RUNNERS + -###### npm - -* [Introduction to NPM Scripts](https://www.freecodecamp.org/news/introduction-to-npm-scripts-1dbb2ae01633/) - -###### GRUNT - -* [An Introduction To Grunt](http://code.tutsplus.com/articles/an-introduction-to-grunt--wp-34728) -* [Setting Up Grunt For Your Next Project](http://code.tutsplus.com/articles/an-introduction-to-grunt--wp-34728) - -###### GULP - -* [Gulp](http://gulpjs.com/) -* [LEARNING GULP: GETTING STARTED WITH THE FRONT END FACTORY](http://hmphry.com/gulp/) +
      + 3.10.2 BUILD TOOLS / TASK RUNNERS / MODULE BUNDLERS +
      + +
      #### 3.11 JAVASCRIPT COMPILED LANGUAGES From 2be9207d73cca9d0c6fdf85f57d3e348ed25b34e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 2 May 2020 00:00:51 +0300 Subject: [PATCH 151/227] Update README.md --- README.md | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1cf573d..6d99e73 100755 --- a/README.md +++ b/README.md @@ -1039,27 +1039,38 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.11 JAVASCRIPT COMPILED LANGUAGES -##### 3.11.1 COFFEESCRIPT - - * [**CoffeeScript** Official site](http://coffeescript.org/) - * [**Hard Rock CoffeeScript**](http://hardrockcoffeescript.org/) - * [**CoffeeScript: The beautiful way to write JavaScript**, By Amir Salihefendic](http://amix.dk/blog/post/19612) - -> TOOLS - -* [**CoffeePad**: CoffeeScript editor in your browser](https://github.com/gokmen/coffeepad) - -> VIDEOS - -* [**Rise of the Transpilers** by Jeremy Ashkenas](https://www.youtube.com/watch?v=DspYurD75Ns) - -> BOOKS +
      + 3.11.1 COFFEESCRIPT +
      + +
      +

      TOOLS

      +
      + +
      +

      VIDEOS

      +
      + +
      +

      BOOKS

      +
      + +
      - * [**CoffeeScript Cookbook**](https://coffeescript-cookbook.github.io/) - * [**CoffeeScript Ristretto**](https://leanpub.com/coffeescript-ristretto/read) - * [**Smooth CoffeeScript**](http://autotelicum.github.io/Smooth-CoffeeScript/) - * [**The Little Book on CoffeeScript**](http://arcturo.github.io/library/coffeescript/) - * [**CoffeeScript: The Good Parts**, By Azat Mardan](https://qconnewyork.com/ny2014/system/files/presentation-slides/CoffeeScript-The-Good-Parts.pdf) ##### 3.11.2 TYPESCRIPT [ UPDATED ] From 4d1ea483dce247de90648cf2b859ba1d5e2b04e6 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 2 May 2020 00:03:23 +0300 Subject: [PATCH 152/227] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d99e73..d78544f 100755 --- a/README.md +++ b/README.md @@ -1069,8 +1069,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    • The Little Book on CoffeeScript
    • CoffeeScript: The Good Parts, By Azat Mardan
    -
    - +
    ##### 3.11.2 TYPESCRIPT [ UPDATED ] From 0ed5bc65f0eebc55d6140092a9e265d6b1df9d1a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 2 May 2020 17:50:48 +0300 Subject: [PATCH 153/227] 02.05.2020 - Adding video on Promises by MPJ --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d78544f..2cc453f 100755 --- a/README.md +++ b/README.md @@ -215,6 +215,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g > TAMING THE ASYNCHRONOUS NATURE OF JAVASCRIPT [ UPDATED ] * [**Asynchronous JavaScript: From Callback Hell to Async and Await**](https://www.toptal.com/javascript/asynchronous-javascript-async-await-tutorial) +* [**Promises** YouTube video by MPJ (Fun Fun Function) | 17min ](https://www.youtube.com/watch?v=2d7s3spWAzo) * [**Promises, async/await @ JavaScript.info**](https://javascript.info/async) * [**Async JS Crash Course - Callbacks, Promises, Async Await** YouTube video by Traversy Media | 25min ](https://www.youtube.com/watch?v=PoRJizFvM7s) * [**async / await in JavaScript - What, Why and How - Fun Fun Function** YouTube video | 24min](https://www.youtube.com/watch?v=568g8hxJJp4) From df451792427b8598208329f9b74778d60865d796 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 10 May 2020 06:03:12 +0300 Subject: [PATCH 154/227] 10.05.2020 - Adding JavaScript Basics --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2cc453f..823e916 100755 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**JavaScript First Steps** | MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps) * [**What Is JavaScript Made Of?**](https://overreacted.io/what-is-javascript-made-of/) * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) +* [**JavaScript Basics** A quick introduction to basic and important concepts of JavaScript](https://fkling.github.io/jsbasics/) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) * [JavaScript Guide, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) From ab50e8ddebbe3ef21b97e8ca6c768c0322395add Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 15 Sep 2020 14:41:37 +0300 Subject: [PATCH 155/227] Adding the amazing Tania Rascia in the list of Developers to follow --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 823e916..fe9ece6 100755 --- a/README.md +++ b/README.md @@ -977,6 +977,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + Tania Rascia + [Twitter](https://twitter.com/taniarascia) + Blog + + + Thomas Fuchs Twitter Blog From 4b30f5674a061690a847efac5889cdb08a4d9305 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 15 Sep 2020 14:43:15 +0300 Subject: [PATCH 156/227] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe9ece6..e5a05f2 100755 --- a/README.md +++ b/README.md @@ -972,13 +972,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g Stoyan Stefanov - [Twitter](Stoyan Stefanov) + Twitter Blog Tania Rascia - [Twitter](https://twitter.com/taniarascia) + Twitter Blog From 59129fd244b9e08854b6437bd8d691a4df78399d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 3 Oct 2020 21:48:53 +0300 Subject: [PATCH 157/227] 03.10.2020 - Adding a bit of advice for semicolons --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e5a05f2..92f8288 100755 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * If you are using [VSCode Editor](https://code.visualstudio.com/) add the `//@ts-check` comment at the top of your code for an extra layer of checks via [TypeScript Linter](https://raw.githubusercontent.com/kostasx/LearnJavascript/master/img/ts-check.jpg) * [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonely Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) + * [Here's a little bit of **advice for the semicolon haters** (by Ben Alman)](http://benalman.com/news/2013/01/advice-javascript-semicolon-haters/) * [Prefer using `===` over `==`](http://bonsaiden.github.io/JavaScript-Garden/#types.equality) * Always use curly braces * Comment your code From 31d5178f4ce8645b3f0615cacd5adf5ee80d8a39 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 2 Feb 2021 22:49:10 +0200 Subject: [PATCH 158/227] Added resource for undefined vs null --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92f8288..da6d2c0 100755 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [JavaScript Visualized: Scope (Chain)](https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd), By [Lydia Hallie](https://github.com/lydiahallie) * [**Scope in JavaScript** - HTTP 203](https://www.youtube.com/watch?v=5LEuJNLfLN0) +* Understand how `undefined` and `null` behave and how [they differ](https://2ality.com/2021/01/undefined-null-revisited.html). * Study `Objects`, [`Prototypal Inheritance` and `OOP`](http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/), and [**OLOO**: Objects Linked to Other Objects](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md) * [**JavaScript Visualized: Prototypal Inheritance**](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) * [**Marcus Phillips: Secrets and Lies about JavaScript classes** (Low-quality video but definitely worth watching)](https://www.youtube.com/watch?v=_JJgSbuj5VI) From a0db9d267cf7bb82774ece2c5bdf8a41293cba33 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 2 Feb 2021 22:53:17 +0200 Subject: [PATCH 159/227] Added Jake Archibald to the list of people to follow --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index da6d2c0..000510a 100755 --- a/README.md +++ b/README.md @@ -859,6 +859,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + Jake Archibald + Twitter + Blog + YouTube + + + Jeremy Ashkenas Twitter Blog From ca6a519ce9ff1e15a116f9b15cdbb1f54e9ce5c8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 28 Oct 2021 01:03:33 +0200 Subject: [PATCH 160/227] Added video resource for Closures, This and Execution Context --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 000510a..1118ae8 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Watch every video by [Douglas Crockford](https://www.youtube.com/results?search_query=douglas+crockford). * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) + * [**JavaScript the Hard Parts: Closures, Scope, and Execution Context** (Student Approved Resource)](https://www.youtube.com/watch?v=ZVXrJ4dnUxM) * [**Understanding Functions and 'this'** By Bryan Hughes](https://www.youtube.com/watch?v=AOSYY1_np_4) * Study performance and code optimization @@ -1110,4 +1111,6 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### Contributors +A big thanks to [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. + Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. From 898c887b0496844a18f5e6b94232de144476ecd9 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 2 Nov 2021 15:26:56 +0100 Subject: [PATCH 161/227] Added 2 newsletters suggestions and Umar Hansa --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 1118ae8..1123fed 100755 --- a/README.md +++ b/README.md @@ -689,6 +689,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
  • A Drip of JavaScript
  • Node Weekly
  • ng-newsletter: The free, weekly newsletter of the best AngularJS content on the web
  • +
  • Developer Tips by Umar [ HIGHLY RECOMMENDED ]
  • +
  • ES.next News - The latest in JavaScript and cross-platform tools
  • @@ -1005,6 +1007,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + Umar Hansa + Twitter + Blog + YouTube + + Yehuda Katz Twitter Blog From e37bc933b8bd9c63de7014614c2bda089921f94a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 3 Nov 2021 14:27:42 +0100 Subject: [PATCH 162/227] Added Kent C. Dodds in the list of developers to follow --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 1123fed..a5f1632 100755 --- a/README.md +++ b/README.md @@ -905,6 +905,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + Kent C. Dodds (If you are learning React, you definitely want to follow this person) + Twitter + Blog + YouTube + + Kyle Simpson Twitter Blog From 2d055b502e8379493d06fea6049d7c7271d264e5 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 6 Apr 2022 11:12:19 +0200 Subject: [PATCH 163/227] Added Asim Hussain to the list of developers to follow --- README.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a5f1632..f691fb7 100755 --- a/README.md +++ b/README.md @@ -697,7 +697,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    +
    3.9 DEVELOPERS (TWITTER, BLOGS) @@ -711,8 +711,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - - + + @@ -724,37 +724,44 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - + - + - - + + + + + + + + + - - + + - - + + - - + + From 87784b92e58e53fe11b4c3723324b5bf7b4201b5 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 20 Apr 2022 09:48:50 +0200 Subject: [PATCH 164/227] Add YouTube channel for Dan Abramov --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f691fb7..9b95f0f 100755 --- a/README.md +++ b/README.md @@ -804,7 +804,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - + From 115a6de9b93a9f0c610179dc581163341f3b1cbd Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 10 May 2022 21:57:34 +0200 Subject: [PATCH 165/227] Add awesome video by Tejas Kumar on Deconstructing React --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9b95f0f..8f1d716 100755 --- a/README.md +++ b/README.md @@ -574,6 +574,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g ReactJS Tutorial for Beginners [YouTube Playlist]
  • The React Cheatsheet for 2020 (+ real-world examples)
  • +
  • Deconstructing React (⭐⭐⭐⭐⭐ Pure gold!) Tejas Kumar | 40min
  • Introduction to React YouTube video by Le Wagon | 1h 53min
  • Learn React in 48 interactive screencasts
  • Getting Started With React
  • From eb1d344d1eba73a844a68b3d263f736d846de5e6 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 11 May 2022 21:41:49 +0200 Subject: [PATCH 166/227] Create array.prototype.reduce.js --- deconstructing/array.prototype.reduce.js | 159 +++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 deconstructing/array.prototype.reduce.js diff --git a/deconstructing/array.prototype.reduce.js b/deconstructing/array.prototype.reduce.js new file mode 100644 index 0000000..0ecffa1 --- /dev/null +++ b/deconstructing/array.prototype.reduce.js @@ -0,0 +1,159 @@ +// Code accompanying the video "Deconstructing Array.prototype.reduce": https://youtu.be/s02LsdIKhVY + +// Array.prototype.reduce = function(){ ... } +Array.prototype.myReduce = myReduce; // <= We should never do this! + +const output = [10,20,30].reduce(function( acc, val, index ){ + // console.log( index, val ); + acc["index_"+index] = { value: val * 2 }; + return acc; +}, {}); +console.log( "output", output ); + +function myReduce( transformFn, initialValue ){ + let accumulator = ( typeof initialValue === "undefined") ? this[0] : initialValue; + for ( let index = 0; index < this.length; index++ ){ + if ( typeof initialValue === "undefined" && index === 0 ) { + continue; + } + accumulator = transformFn( accumulator, this[index], index, this ); + } + return accumulator; +} + +function sum( acc, val, index, arr ){ + acc += val; + return acc; +} +[1,2,3,4,10,100].reduce( sum, 0 ); + +const arr = [[1,2],[3,4],[5]]; +const flatten = ( acc, val, index, arr )=>{ + for ( const el of val ){ + acc.push( el ); + } + return acc; +} +arr.reduce( flatten, [] ); + +function averageNumbers( acc, val, index, arr ){ + console.log( acc, val, index ); + acc += val; + if ( index === (arr.length - 1) ){ + acc = acc / arr.length; + } + return acc; +} +// const res = [10,20,30].myReduce( averageNumbers ); +// console.log("avg", res); + +// 8) ArrayOfArrays => FlatArray +// const arr = [[1,2],[3,4],[5]]; +// const flatten = ( acc, val, index, arr )=>{ +// for ( const el of val ){ +// acc.push( el ); +// } +// return acc; +// } +// const flatArray = reduce( arr, flatten, [] ); +// console.log( flatArray ); //=> [1,2,3,4,5]; + +// 7) +function reduce( array, transformFn, initialValue ){ + // If the initialValue is empty (undefined) + // I am going to pick the first value from the array as the initialValue + let accumulator = ( typeof initialValue === "undefined") ? array[0] : initialValue; + for ( let index = 0; index < array.length; index++ ){ + if ( typeof initialValue === "undefined" && index === 0 ) { + continue; + } + accumulator = transformFn( accumulator, array[index], index, array ); + } + return accumulator; +} +// const nums = [ 10, 20, 30 ]; +// function averageNumbers( acc, val, index, arr ){ +// console.log( acc, val, index ); +// acc += val; +// if ( index === (arr.length - 1) ){ +// acc = acc / arr.length; +// } +// return acc; +// } +// const avg = reduce( nums, averageNumbers ); +// console.log( avg ); //=> 20 + + +// 6) Array => Transformation => SomeValue +// function reduce( array, transformFn, initialValue ){ +// let accumulator = initialValue; +// for ( const val of array ){ +// accumulator = transformFn( accumulator, val ); +// } +// return accumulator; +// } +// const nums = [ 10, 20, 30 ]; +// function summingUpNumbers( acc, val ){ +// acc += val; +// return acc; +// } +// const sum = reduce( nums, summingUpNumbers, 0 ); +// console.log( sum ); + +// 5) Reduce: +// 1) Accept an Array as an input +// 2) Initialize an Accumulator with some value (or none) +// 3) Iterate over Array values +// 5) Modify Accumulator based on each value from the Array +// 6) Return the Accumulator + +// 4) ArrayOfArrays => FlatArray +// const arr = [[1,2],[3,4],[5]]; // => [1,2,3,4,5] +// function reduce( array ){ +// let flatArray = []; +// for ( const innerArray of array ){ +// // console.log( innerArray ); +// for ( const innerVal of innerArray ){ +// // console.log( innerVal ); +// flatArray.push( innerVal ); +// } +// } +// return flatArray; +// } +// const result = reduce( arr ); +// console.log( result ); + +// 3) ArrayOfNumbers => Average +// const nums = [ 10, 20, 30 ]; +// function reduce( array ){ +// let avg; +// let sum = 0; +// // Iterate over the values of the input array +// // Accumulate the values and operate on these values +// for ( const val of array ){ +// sum += val; +// } +// avg = sum / array.length; +// return avg; +// } +// const avg = reduce( nums ); +// console.log( avg ); + +// 2) ArrayOfNumbers => SumOfNumbers +// const nums = [ 10, 20, 30 ]; +// function reduce( array ){ +// let sum = 0; +// // Accumulate the values from the input array +// for ( const val of array ){ +// sum = sum + val; +// } +// return sum; +// } +// const result = reduce( nums ); +// console.log( result ); //=> 60 + +// 1) Number => String +// function reduce( number ){ +// return String(number); +// } +// reduce(42); From 953dbc19fb1aaa9b1a893e85c739b9727a29bb46 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 11 May 2022 22:10:17 +0200 Subject: [PATCH 167/227] Update array.prototype.reduce.js --- deconstructing/array.prototype.reduce.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/deconstructing/array.prototype.reduce.js b/deconstructing/array.prototype.reduce.js index 0ecffa1..9e70d79 100644 --- a/deconstructing/array.prototype.reduce.js +++ b/deconstructing/array.prototype.reduce.js @@ -47,6 +47,7 @@ function averageNumbers( acc, val, index, arr ){ // const res = [10,20,30].myReduce( averageNumbers ); // console.log("avg", res); + // 8) ArrayOfArrays => FlatArray // const arr = [[1,2],[3,4],[5]]; // const flatten = ( acc, val, index, arr )=>{ @@ -58,6 +59,7 @@ function averageNumbers( acc, val, index, arr ){ // const flatArray = reduce( arr, flatten, [] ); // console.log( flatArray ); //=> [1,2,3,4,5]; + // 7) function reduce( array, transformFn, initialValue ){ // If the initialValue is empty (undefined) @@ -100,12 +102,15 @@ function reduce( array, transformFn, initialValue ){ // const sum = reduce( nums, summingUpNumbers, 0 ); // console.log( sum ); -// 5) Reduce: + +// 5) Reduce: Pseudo Code / Pattern + // 1) Accept an Array as an input // 2) Initialize an Accumulator with some value (or none) // 3) Iterate over Array values -// 5) Modify Accumulator based on each value from the Array -// 6) Return the Accumulator +// 4) Modify Accumulator based on each value from the Array +// 5) Return the Accumulator + // 4) ArrayOfArrays => FlatArray // const arr = [[1,2],[3,4],[5]]; // => [1,2,3,4,5] @@ -123,6 +128,7 @@ function reduce( array, transformFn, initialValue ){ // const result = reduce( arr ); // console.log( result ); + // 3) ArrayOfNumbers => Average // const nums = [ 10, 20, 30 ]; // function reduce( array ){ @@ -139,6 +145,7 @@ function reduce( array, transformFn, initialValue ){ // const avg = reduce( nums ); // console.log( avg ); + // 2) ArrayOfNumbers => SumOfNumbers // const nums = [ 10, 20, 30 ]; // function reduce( array ){ @@ -152,6 +159,7 @@ function reduce( array, transformFn, initialValue ){ // const result = reduce( nums ); // console.log( result ); //=> 60 + // 1) Number => String // function reduce( number ){ // return String(number); From 0a393dc81ece41b7c70fd95f798bc79d0bf0bc43 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 14 May 2022 21:31:51 +0200 Subject: [PATCH 168/227] Add YouTube video on Closures by Nicholas Johnson --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8f1d716..f68ec6b 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Watch every video by [Douglas Crockford](https://www.youtube.com/results?search_query=douglas+crockford). * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) * Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) + * [**JavaScript Closures are Super Simple!** (YouTube Video: 16 min)](https://www.youtube.com/watch?v=2cRjcXwsG0I) * [**JavaScript the Hard Parts: Closures, Scope, and Execution Context** (Student Approved Resource)](https://www.youtube.com/watch?v=ZVXrJ4dnUxM) * [**Understanding Functions and 'this'** By Bryan Hughes](https://www.youtube.com/watch?v=AOSYY1_np_4) From 88822822d8b6d844358323cbb4c2d37299c03bde Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 23 Jun 2022 16:51:10 +0200 Subject: [PATCH 169/227] Add Basarat Ali Syed --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f68ec6b..026c834 100755 --- a/README.md +++ b/README.md @@ -767,6 +767,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    + + + + + + @@ -1080,6 +1086,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.11 JAVASCRIPT COMPILED LANGUAGES +##### 3.11.2 TYPESCRIPT [ UPDATED ] + + * [TypeScript](https://www.typescriptlang.org/) + * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) + * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) + * [**Advanced TypeScript Tutorials** by Basarat Ali Syed on YouTube](https://www.youtube.com/playlist?list=PLYvdvJlnTOjF6aJsWWAt7kZRJvzw-en8B) +
    3.11.1 COFFEESCRIPT
    @@ -1112,12 +1125,6 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    -##### 3.11.2 TYPESCRIPT [ UPDATED ] - - * [TypeScript](https://www.typescriptlang.org/) - * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) - * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) -
    3.11.3 ELM

    Elm

    From fb9bd63a8579772f218fe78d563c6709468df05c Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 23 Jun 2022 16:53:16 +0200 Subject: [PATCH 170/227] Add Axel Rauschmayer TS tutorial --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 026c834..10d597d 100755 --- a/README.md +++ b/README.md @@ -1088,6 +1088,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g ##### 3.11.2 TYPESCRIPT [ UPDATED ] + * [Understanding TypeScript’s type notation (**Probably the best resource to begin with TS**)](https://2ality.com/2018/04/type-notation-typescript.html) * [TypeScript](https://www.typescriptlang.org/) * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) From 0b8b963c8dfc91c8700e9c6acca341c4fb91a79f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 23 Jun 2022 16:54:50 +0200 Subject: [PATCH 171/227] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10d597d..184211a 100755 --- a/README.md +++ b/README.md @@ -767,7 +767,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - + From bc0ea64e0f5bb8a4b62a021338a7d729710ac04d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 24 Jun 2022 12:33:03 +0200 Subject: [PATCH 172/227] Add Ben Lesh to list of developers to follow --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 184211a..83800d0 100755 --- a/README.md +++ b/README.md @@ -791,6 +791,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g + + + + + + From d763abe35b02bef6fd233a1fab7de2eedfef27c5 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 4 Jul 2022 12:31:16 +0200 Subject: [PATCH 173/227] Add Node.dev --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 83800d0..0cb15f3 100755 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - +
    EXPRESS.JS @@ -342,7 +342,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    ADVANCED @@ -357,7 +357,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    BOOKS @@ -368,7 +368,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    VIDEOS (Updated) @@ -385,7 +385,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    TESTING CODE @@ -395,7 +395,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    HOSTING PLATFORMS @@ -413,7 +413,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - +
    CERTIFICATES @@ -424,6 +424,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    + + +##### Resources + +- [Node.dev: Your latest Node.js content, news and updates in one place.](https://node.dev/) +

    3.4 FRAMEWORKS AND LIBRARIES

    From a7829f79a522b8d9f3e4412b0016337141d6e3b6 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 4 Jul 2022 12:33:18 +0200 Subject: [PATCH 174/227] Update structure --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0cb15f3..b4ece70 100755 --- a/README.md +++ b/README.md @@ -426,9 +426,16 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -##### Resources +
    + RESOURCES +
    + +
    -- [Node.dev: Your latest Node.js content, news and updates in one place.](https://node.dev/) +--- From 2300a91f9ad7a54bf7fadb89cff069a7cef202da Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 8 Jul 2022 11:09:07 +0200 Subject: [PATCH 175/227] Add Design Patterns video --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b4ece70..140ab21 100755 --- a/README.md +++ b/README.md @@ -72,10 +72,11 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [Debugging JavaScript](https://developer.chrome.com/devtools/docs/javascript-debugging) * [Chrome DevTools Command Line API Reference](https://developer.chrome.com/devtools/docs/commandline-api) -* Study JavaScript Patterns and Anti-Patterns: +* Study general Software Design Patterns along with JavaScript-specific Patterns and Anti-Patterns: * [Learning JavaScript Design Patterns, Addy Osmani](http://addyosmani.com/resources/essentialjsdesignpatterns/book/) * [Javascript Patterns, moderated by *Shi Chuan*](http://shichuan.github.io/javascript-patterns/) + * [Frontend architecture: Decoupling apps from frameworks (DevFest 2019)](https://www.youtube.com/watch?v=jmcx3b78V8s) * Understand the Single Threaded Process & Event Loop. From 6d76ae0f113cbc6abbf54217e7c29edeff4d7e4c Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 29 Aug 2022 11:22:04 +0300 Subject: [PATCH 176/227] Add Headings for Newsletters & Developers --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 140ab21..d5127da 100755 --- a/README.md +++ b/README.md @@ -694,6 +694,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g +### 3.8 NEWSLETTERS +
    3.8 NEWSLETTERS @@ -713,6 +715,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g +### 3.9 DEVELOPERS +
    3.9 DEVELOPERS (TWITTER, BLOGS)
    Addy OsmaniTwitterBlogTwitterBlog
    Alex Castrounis TwitterBlogBlog
    Aria Stewart TwitterBlogBlog
    Ariya HidayatTwitterBlogTwitterBlog YouTube
    Asim HussainTwitterBlogYouTube
    Axel RauschmayerTwitterBlogTwitterBlog
    Andrea GiammarchiTwitterBlogTwitterBlog
    Angus CrollTwitterBlogTwitterBlog
    Dan Abramov Twitter BlogYouTube
    Basarat AlisyedTwitterBlogYouTube
    Béla Varga Twitter
    Basarat AlisyedBasarat Ali Syed Twitter Blog YouTube
    Ben LeshTwitterBlogYouTube
    Brendan Eich Twitter Blog
    From d398402d570f99807e6478e57b503a2d362f0ff0 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 29 Aug 2022 22:23:37 +0300 Subject: [PATCH 177/227] Remove javascripting.com broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5127da..a6d783a 100755 --- a/README.md +++ b/README.md @@ -447,7 +447,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g From 0436908048a03e50e32a7d91f020c178cb015bea Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 29 Aug 2022 23:00:03 +0300 Subject: [PATCH 178/227] Add ToC. Update Hosting section. Add NBB API Guide. --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a6d783a..add312d 100755 --- a/README.md +++ b/README.md @@ -8,6 +8,32 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g --- +### Table of Contents (wip) + +- [1. Learning](#learning) +- [2. Suggested Coding Rules & Best Practices](#2-suggested-coding-rules-and-best-practices) +- [3. Resources](#3-resources) + - [3.1 Beginner Resources](#31-beginner-resources--updated) + - [3.1.1 Courses](#311-courses--updated) + - [3.1.2 Testing Code](#312-testing-code) + - [3.1.3 APIs](#313-apis) + - [3.2 General Resources](#32-general) + - [Functional Programming](#functional-programming-reading) + - [Reading](#functional-programming-reading) + - [Video](#functional-programming-video) + - [Video](#functional-programming-libraries) + - [3.3 Node.JS](#33-nodejs) + - [3.4 Frameworks & Libraries](#34-frameworks-and-libraries) + - [3.5 Books](#35-books) + - [3.6 Online Books](#36-online-books) + - [3.7 Video & Audio](#37-video--audio) + - [3.8 Newsletters](#38-newsletters) + - [3.9 Developers](#39-developers) + - [work in progres...](#) +- [Contributors](#contributors) + +--- + **About me**: [GitHub](https://github.com/kostasx/) | [Twitter](https://twitter.com/kostas_mns) | [StackOverflow](https://stackoverflow.com/users/4861760/kostasx) | [LinkedIn](https://www.linkedin.com/in/kostas-minaidis/) | [PlethoraThemes](https://plethorathemes.com/author/kostasx/) --- @@ -169,19 +195,19 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**How to Become a Great JavaScript Developer***](http://blog.ustunozgur.com/javascript/programming/books/videos/2015/06/17/how_to_be_a_great_javascript_software_developer.html) * [FAQ for comp.lang.javascript](http://pointedears.de/scripts/faq/cljs/) -> COURSES [ UPDATED ] +##### 3.1.1 COURSES [ UPDATED ] * [**Deep Dive Into Modern Web Development** | Full Stack course by The University of Helsinki ](https://fullstackopen.com/en/) * [CodeCombat: Learn to Code by Playing a Game](http://codecombat.com/) * [Javascript for Beginners, Udemy **PAID COURSE**](https://www.udemy.com/beginning-javascript/) -> TESTING CODE +##### 3.1.2 TESTING CODE * [JSBin](http://jsbin.com/) * [JSFiddle](http://jsfiddle.net/) • [16 ONLINE JS EDITORS](http://codecondo.com/16-online-javascript-editors-for-web-developers/) -> APIs +##### 3.1.3 APIs * [**Web APIs**](https://developer.mozilla.org/en-US/docs/Web/API) * [A presentation about several HTML5 APIs](http://eventloop.gr/frontend/html5/apis.html#/) (Contains useful links, code and resources in the slides) @@ -234,8 +260,8 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    - DEBUGGING + DEBUGGING
    • Debugging JavaScript
    • Chrome DevTools Command Line API Reference
    • @@ -273,7 +299,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [MetaJS: Visualize JavaScript AST Execution](http://int3.github.io/metajs/) * [Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014](https://www.youtube.com/watch?v=8aGhZQkoFbQ) -> FUNCTIONAL PROGRAMMING: **READING** +#### FUNCTIONAL PROGRAMMING: **READING** * [**An Intro to Functional Programming Concepts in JavaScript**, By Thomas Collardeau ](https://medium.com/@collardeau/intro-to-functional-programming-concepts-in-javascript-b0650773139c#.8bp7dkyvm) * [**Why Curry Helps**, By Hugh FD Jackson](https://hughfdjackson.com/javascript/why-curry-helps/) @@ -282,7 +308,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**Functional Programming**, By Scott Sauyet](http://scott.sauyet.com/Javascript/Talk/FunctionalProgramming/) * [Ramda + ramda-fantasy REPL](https://github.com/hemanth/ramda-repl) -> FUNCTIONAL PROGRAMMING: **VIDEO** +#### FUNCTIONAL PROGRAMMING: **VIDEO** * [**Intro to Recursion - Refactoring to a Pure Function**, By Shanon Osbourne](https://egghead.io/lessons/javascript-intro-to-recursion-the-problem) * [**Functional programming and curry cooking in JS** By Stefanie Schirmer | JSConf EU 2015**](https://www.youtube.com/watch?v=6Qx5ZAbfqjo) @@ -290,13 +316,13 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**Pure, functional JavaScript**, By Christian Johansen](https://vimeo.com/43382919) * [**Lenses Quick n' Dirty: Functional Lenses in javascript**, By Brian Lonsdorf](https://vimeo.com/104807358) -> FUNCTIONAL PROGRAMMING: **LIBRARIES** +#### FUNCTIONAL PROGRAMMING: **LIBRARIES** * [**Ramda.JS**](http://ramdajs.com/) * [**Lodash.JS**](https://lodash.com/) * [**Underscore.JS**](http://underscorejs.org/) -#### 3.3 NODE +#### 3.3 NODEJS
      BEGINNERS (Updated) @@ -355,6 +381,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    • Comparison: Tools to Automate Restarting Node.js Server After Code Changes
    • Error Handling in Node.js
    • Using Node.js to join audio files
    • +
    • REST API Design Guide (National Bank of Belgium)
    @@ -399,18 +426,22 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    - HOSTING PLATFORMS + HOSTING PLATFORMS (Updated)
    @@ -440,7 +471,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -

    3.4 FRAMEWORKS AND LIBRARIES

    +#### 3.4 FRAMEWORKS AND LIBRARIES

    LISTS

    @@ -624,9 +655,11 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g +#### 3.5 **BOOKS** +
    - 3.5 BOOKS + See Books
      @@ -642,9 +675,11 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g +#### 3.6 **ONLINE BOOKS** +
      - 3.6 ONLINE BOOKS + See Online Books
      From f3e0932424e28714617e0e2221c3c80bf1dbdc03 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 31 Aug 2022 16:08:57 +0300 Subject: [PATCH 180/227] Update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91b727d..335dd1d 100755 --- a/README.md +++ b/README.md @@ -605,7 +605,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
      +
      3.4.6 REACT.JS [ UPDATED ] From db8d98685075376d13ca255918d5526393f0723f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 31 Aug 2022 16:14:19 +0300 Subject: [PATCH 181/227] Update TS section --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 335dd1d..0f65e09 100755 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - [3.8 Newsletters](#38-newsletters) - [3.9 Developers](#39-developers) - [work in progres...](#) + - [3.10 Tools](#310-tools) + - [3.11 JS Compiled Languages](#311-javascript-compiled-languages) + - [Contributors](#contributors) --- @@ -1174,6 +1177,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**TypeScript Tutorial for Angular and React Developers** | Mosh | YouTube video | 52min ](https://www.youtube.com/watch?v=NjN00cM18Z4) * [**TypeScript Tutorial** by Derek Banas | YouTube video | 49min ](https://www.youtube.com/watch?v=-PR_XqW9JJU) * [**Advanced TypeScript Tutorials** by Basarat Ali Syed on YouTube](https://www.youtube.com/playlist?list=PLYvdvJlnTOjF6aJsWWAt7kZRJvzw-en8B) + * [⭐ An interactive TypeScript tutorial for beginners](https://github.com/total-typescript/beginners-typescript-tutorial) + * [Matt's Advanced TypeScript workshop](https://github.com/total-typescript/advanced-typescript-workshop) +
      3.11.1 COFFEESCRIPT From 33c3771eebd89c58d3d73772b4194e67c2018c06 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 31 Aug 2022 16:17:34 +0300 Subject: [PATCH 182/227] Mandatory TS note --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0f65e09..e29d8a7 100755 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Start building cross-platform Desktop Applications using HTML/CSS/JavaScript in [Electron.JS](https://electronjs.org/) or [Node-Webkit](https://nwjs.io/). * Follow some of the masters on Twitter. *(See `section 3.6` of the RESOURCES section)* * Code using a [Style Guide](https://github.com/airbnb/javascript). +* [And, of course, you definitely need to learn **TypeScript**!](#3112-typescript--updated) ### 2. SUGGESTED CODING RULES AND BEST PRACTICES From a6b20702288b33f47e24fde90134025963c7d4f5 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 1 Sep 2022 11:29:45 +0300 Subject: [PATCH 183/227] Add Explore JavaScript --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e29d8a7..dc6a4ee 100755 --- a/README.md +++ b/README.md @@ -232,6 +232,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [JavaScript the Right Way](http://jstherightway.org/) * [JavaScript and the Browser: Under the Hood, *by Ariya Hidayat*](https://speakerdeck.com/ariya/javascript-and-the-browser-under-the-hood) * [JSdo It: Share JavaScript, HTML5 and CSS.](http://jsdo.it/) +* [Explore JavaScript: Popular and new libraries, top authors and trending discussions](https://kandi.openweaver.com/explore/javascript) > ES6 (EcmaScript 2015) From 744a914caa8b8375a5fd0a19cb7531c02ea5f9d4 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Fri, 30 Sep 2022 20:22:26 +0300 Subject: [PATCH 184/227] Add TypeScript Podcast. Add Podcasts section. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc6a4ee..8f889f4 100755 --- a/README.md +++ b/README.md @@ -753,6 +753,11 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [The Origin of Javascript with Brendan Eich](http://javascriptjabber.com/124-jsj-the-origin-of-javascript-with-brendan-eich/) * [5 Minutes of JavaScript](http://five-js.envylabs.com/) +##### 3.7.3 AUDIO > PODCASTS + +* [CoRecursive: The Stories Behind The Code](https://corecursive.com/) +* [CaSE: Conversations about Software Engineering](https://www.case-podcast.org/) + @@ -1181,7 +1186,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**Advanced TypeScript Tutorials** by Basarat Ali Syed on YouTube](https://www.youtube.com/playlist?list=PLYvdvJlnTOjF6aJsWWAt7kZRJvzw-en8B) * [⭐ An interactive TypeScript tutorial for beginners](https://github.com/total-typescript/beginners-typescript-tutorial) * [Matt's Advanced TypeScript workshop](https://github.com/total-typescript/advanced-typescript-workshop) - + * [🎙️ Podcast: **Using TypeScript Like A Pro**, Chris Krycho](https://corecursive.com/034-chris-krycho-typescript/)
      3.11.1 COFFEESCRIPT From b7d0d6161b2d943daa2798d5236eabb991c3ab89 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 26 Jan 2023 11:03:08 +0200 Subject: [PATCH 185/227] Updated Angular resources --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8f889f4..83c5930 100755 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g --- -### Table of Contents (wip) +### Table of Contents (work in progress) - [1. Learning](#learning) - [2. Suggested Coding Rules & Best Practices](#2-suggested-coding-rules-and-best-practices) @@ -24,6 +24,12 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - [Video](#functional-programming-libraries) - [3.3 Node.JS](#33-nodejs) - [3.4 Frameworks & Libraries](#34-frameworks-and-libraries) + - [3.4.1 Backbone.js](#backbonejs) + - [3.4.2 jQuery](#) + - [3.4.3 Angular & AngularJS](#) + - [3.4.4 Ember](#) + - [3.4.5 Knockout.js](#) + - [3.4.6 React.js](#) - [3.5 Books](#35-books) - [3.6 Online Books](#36-online-books) - [3.7 Video & Audio](#37-video--audio) @@ -487,7 +493,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g
    • Awesome JavaScript: A collection of awesome browser-side JavaScript libraries, resources and shiny things
    -
    +
    3.4.1 BACKBONE.JS

      @@ -545,7 +551,55 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g

      TUTORIALS: ANGULAR

    From 523894d330be582a9921466d8862d0fc46419ffc Mon Sep 17 00:00:00 2001 From: Terence Grover Date: Mon, 13 Feb 2023 11:53:02 +0100 Subject: [PATCH 186/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83c5930..6af7a4e 100755 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Comment your code * [Avoid/Reduce global variables](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * Avoid using `with()` -* [Avoid using `eval()`](http://bonsaiden.github.io/JavaScript-Garden/#core.eval) +* [Avoid using `eval()`](https://dev.to/spukas/everything-wrong-with-javascript-eval-35on) * Pass functions instead of strings to setTimeout or setInterval, as this triggers eval() internally. * Use [JSHint](http://www.jshint.com/) / [JSLint](http://www.jslint.com/) * If you are using [VSCode](https://code.visualstudio.com/) you can add the following comment at the top of your code to enable type checking: `//@ts-check` ([See screenshot](https://github.com/kostasx/LearnJavascript/blob/master/README.md#tscheck)) From a4cca337c61a56deb5186513dc199d564733cc6b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 14 Feb 2023 01:16:47 +0200 Subject: [PATCH 187/227] Terence Grover Contribution --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83c5930..7c57ee8 100755 --- a/README.md +++ b/README.md @@ -1290,6 +1290,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### Contributors -A big thanks to [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. +A big thanks to: + +- [Terence Grover](https://github.com/TerenceGrover) for spotting broken links and suggesting new ones +- [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. From 59c72513f5c54be41e8b45800065e0e9becf82cf Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 14 Feb 2023 01:20:02 +0200 Subject: [PATCH 188/227] Update JS Garden broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce66d73..dcf17f8 100755 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**The cost of JavaScript in 2019** By Addy Osmani](https://v8.dev/blog/cost-of-javascript-2019) * Check every tip on the [A Drip of JavaScript](http://designpepper.com/js-drip-archive/) list and subscribe to it. -* Check [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/). +* Check the _(archived version)_ of an old but good resource: [JavaScript Garden](https://web.archive.org/web/20160528154422/https://bonsaiden.github.io/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 and 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.* From 297ee2a9d88fe53cc5361b2a68e9d1c4c1a42597 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 27 Feb 2023 13:35:19 +0200 Subject: [PATCH 189/227] Updated Podcasts --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dcf17f8..5552db8 100755 --- a/README.md +++ b/README.md @@ -807,11 +807,22 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [The Origin of Javascript with Brendan Eich](http://javascriptjabber.com/124-jsj-the-origin-of-javascript-with-brendan-eich/) * [5 Minutes of JavaScript](http://five-js.envylabs.com/) -##### 3.7.3 AUDIO > PODCASTS - +##### 3.7.3 AUDIO > PODCASTS 📻 + +* [JavaScript Jabber](https://podcasts.apple.com/us/podcast/javascript-jabber/id1237401284) +* [React Round Up](https://topenddevs.com/podcasts/react-round-up) +* [20MinuteJS](https://20minjs.com/) +* [Kent C Dodds](https://kentcdodds.com/calls) +* [SyntaxFM](https://syntax.fm/) +* [StackOverflow Podcast](https://stackoverflow.blog/podcast/) +* [The Web Platform Podcast](https://thewebplatformpodcast.com/) +* [Software Engineering Radio](https://www.se-radio.net/) +* [IEEE Computing Conversations](https://www.youtube.com/playlist?list=PL4660FB7F523B1770) +* [Command Line Heroes](https://www.redhat.com/en/command-line-heroes) * [CoRecursive: The Stories Behind The Code](https://corecursive.com/) * [CaSE: Conversations about Software Engineering](https://www.case-podcast.org/) - +* [Code Newbie Podcast](https://www.codenewbie.org/podcast) +* [Three Devs and a Maybe](https://threedevsandamaybe.com/) From 1d9d3992ee7701cff227dc5f1be16126e46d1191 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 19 Mar 2023 00:07:27 +0200 Subject: [PATCH 190/227] Enabled Discussions! --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5552db8..b9600c1 100755 --- a/README.md +++ b/README.md @@ -1307,3 +1307,5 @@ A big thanks to: - [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. Kudos to **[Dimitrios Michalakos](http://gr.linkedin.com/in/dmichalakos)** for suggesting the `Understand the Single Threaded Process & Event Loop` section and providing me with the links. + +**Questions? Ideas? Please check out the [Discussions](https://github.com/kostasx/LearnJavascript/discussions) section!** From 604199c18b67b03f1a1140908aeed91bfe746044 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 27 Jun 2023 23:45:45 +0300 Subject: [PATCH 191/227] Added JavaScript-Garden website --- external/JavaScript-Garden/CONTRIBUTING.md | 19 + external/JavaScript-Garden/JavaScript-Garden | 1 + external/JavaScript-Garden/LICENSE | 20 + external/JavaScript-Garden/README.md | 43 + external/JavaScript-Garden/by/index.html | 1525 ++++++++++++++++ .../doc/by/array/constructor.md | 31 + .../JavaScript-Garden/doc/by/array/general.md | 56 + .../JavaScript-Garden/doc/by/core/delete.md | 81 + .../JavaScript-Garden/doc/by/core/eval.md | 47 + .../doc/by/core/semicolon.md | 109 ++ .../doc/by/core/undefined.md | 69 + .../doc/by/function/arguments.md | 130 ++ .../doc/by/function/closures.md | 113 ++ .../doc/by/function/constructors.md | 122 ++ .../doc/by/function/general.md | 48 + .../doc/by/function/scopes.md | 230 +++ .../JavaScript-Garden/doc/by/function/this.md | 118 ++ external/JavaScript-Garden/doc/by/index.json | 69 + .../JavaScript-Garden/doc/by/intro/index.md | 56 + .../doc/by/object/forinloop.md | 63 + .../doc/by/object/general.md | 98 + .../doc/by/object/hasownproperty.md | 55 + .../doc/by/object/prototype.md | 115 ++ .../doc/by/other/timeouts.md | 168 ++ .../JavaScript-Garden/doc/by/types/casting.md | 71 + .../doc/by/types/equality.md | 70 + .../doc/by/types/instanceof.md | 37 + .../JavaScript-Garden/doc/by/types/typeof.md | 80 + external/JavaScript-Garden/doc/de/index.json | 16 + .../JavaScript-Garden/doc/de/intro/authors.md | 8 + .../doc/de/intro/contributors.md | 8 + .../JavaScript-Garden/doc/de/intro/index.md | 42 + .../JavaScript-Garden/doc/de/intro/license.md | 12 + .../doc/en/array/constructor.md | 33 + .../JavaScript-Garden/doc/en/array/general.md | 58 + .../JavaScript-Garden/doc/en/core/delete.md | 85 + .../JavaScript-Garden/doc/en/core/eval.md | 47 + .../doc/en/core/semicolon.md | 114 ++ .../doc/en/core/undefined.md | 74 + .../doc/en/function/arguments.md | 132 ++ .../doc/en/function/closures.md | 116 ++ .../doc/en/function/constructors.md | 127 ++ .../doc/en/function/general.md | 48 + .../doc/en/function/scopes.md | 234 +++ .../JavaScript-Garden/doc/en/function/this.md | 120 ++ external/JavaScript-Garden/doc/en/index.json | 69 + .../JavaScript-Garden/doc/en/intro/index.md | 49 + .../doc/en/object/forinloop.md | 66 + .../doc/en/object/general.md | 99 + .../doc/en/object/hasownproperty.md | 58 + .../doc/en/object/prototype.md | 118 ++ .../doc/en/other/timeouts.md | 172 ++ .../JavaScript-Garden/doc/en/types/casting.md | 71 + .../doc/en/types/equality.md | 71 + .../doc/en/types/instanceof.md | 38 + .../JavaScript-Garden/doc/en/types/typeof.md | 84 + .../doc/es/array/constructor.md | 35 + .../JavaScript-Garden/doc/es/array/general.md | 58 + .../JavaScript-Garden/doc/es/core/delete.md | 87 + .../JavaScript-Garden/doc/es/core/eval.md | 47 + .../doc/es/core/semicolon.md | 114 ++ .../doc/es/core/undefined.md | 74 + .../doc/es/function/arguments.md | 119 ++ .../doc/es/function/closures.md | 98 + .../doc/es/function/constructors.md | 129 ++ .../doc/es/function/general.md | 48 + .../doc/es/function/scopes.md | 231 +++ .../JavaScript-Garden/doc/es/function/this.md | 111 ++ external/JavaScript-Garden/doc/es/index.json | 68 + .../JavaScript-Garden/doc/es/intro/index.md | 46 + .../doc/es/object/forinloop.md | 51 + .../doc/es/object/general.md | 99 + .../doc/es/object/hasownproperty.md | 53 + .../doc/es/object/prototype.md | 116 ++ .../doc/es/other/timeouts.md | 155 ++ .../JavaScript-Garden/doc/es/types/casting.md | 70 + .../doc/es/types/equality.md | 71 + .../doc/es/types/instanceof.md | 38 + .../JavaScript-Garden/doc/es/types/typeof.md | 87 + .../doc/fi/array/constructor.md | 25 + .../JavaScript-Garden/doc/fi/array/general.md | 40 + .../JavaScript-Garden/doc/fi/core/eval.md | 39 + .../doc/fi/core/semicolon.md | 100 + .../doc/fi/core/undefined.md | 54 + .../doc/fi/function/arguments.md | 94 + .../doc/fi/function/closures.md | 78 + .../doc/fi/function/constructors.md | 101 ++ .../doc/fi/function/general.md | 38 + .../doc/fi/function/scopes.md | 190 ++ .../JavaScript-Garden/doc/fi/function/this.md | 91 + external/JavaScript-Garden/doc/fi/index.json | 73 + .../JavaScript-Garden/doc/fi/intro/authors.md | 8 + .../doc/fi/intro/contributors.md | 3 + .../JavaScript-Garden/doc/fi/intro/index.md | 8 + .../JavaScript-Garden/doc/fi/intro/license.md | 9 + .../doc/fi/intro/translators.md | 5 + .../doc/fi/object/forinloop.md | 37 + .../doc/fi/object/general.md | 82 + .../doc/fi/object/hasownproperty.md | 41 + .../doc/fi/object/prototype.md | 80 + .../doc/fi/other/timeouts.md | 114 ++ .../JavaScript-Garden/doc/fi/types/casting.md | 61 + .../doc/fi/types/equality.md | 58 + .../doc/fi/types/instanceof.md | 33 + .../JavaScript-Garden/doc/fi/types/typeof.md | 64 + .../doc/fr/array/constructor.md | 27 + .../JavaScript-Garden/doc/fr/array/general.md | 46 + .../JavaScript-Garden/doc/fr/core/delete.md | 76 + .../JavaScript-Garden/doc/fr/core/eval.md | 42 + .../doc/fr/core/semicolon.md | 100 + .../doc/fr/core/undefined.md | 55 + .../doc/fr/function/arguments.md | 118 ++ .../doc/fr/function/closures.md | 97 + .../doc/fr/function/constructors.md | 109 ++ .../doc/fr/function/general.md | 38 + .../doc/fr/function/scopes.md | 204 +++ .../JavaScript-Garden/doc/fr/function/this.md | 100 + external/JavaScript-Garden/doc/fr/index.json | 69 + .../JavaScript-Garden/doc/fr/intro/index.md | 36 + .../doc/fr/object/forinloop.md | 44 + .../doc/fr/object/general.md | 85 + .../doc/fr/object/hasownproperty.md | 46 + .../doc/fr/object/prototype.md | 85 + .../doc/fr/other/timeouts.md | 130 ++ .../JavaScript-Garden/doc/fr/types/casting.md | 62 + .../doc/fr/types/equality.md | 58 + .../doc/fr/types/instanceof.md | 35 + .../JavaScript-Garden/doc/fr/types/typeof.md | 66 + .../doc/hu/array/constructor.md | 34 + .../JavaScript-Garden/doc/hu/array/general.md | 58 + .../JavaScript-Garden/doc/hu/core/delete.md | 91 + .../JavaScript-Garden/doc/hu/core/eval.md | 49 + .../doc/hu/core/semicolon.md | 119 ++ .../doc/hu/core/undefined.md | 77 + .../doc/hu/function/arguments.md | 120 ++ .../doc/hu/function/closures.md | 99 + .../doc/hu/function/constructors.md | 128 ++ .../doc/hu/function/general.md | 48 + .../doc/hu/function/scopes.md | 234 +++ .../JavaScript-Garden/doc/hu/function/this.md | 110 ++ external/JavaScript-Garden/doc/hu/index.json | 69 + .../JavaScript-Garden/doc/hu/intro/index.md | 41 + .../doc/hu/object/forinloop.md | 53 + .../doc/hu/object/general.md | 90 + .../doc/hu/object/hasownproperty.md | 61 + .../doc/hu/object/prototype.md | 120 ++ .../doc/hu/other/timeouts.md | 167 ++ .../JavaScript-Garden/doc/hu/types/casting.md | 68 + .../doc/hu/types/equality.md | 77 + .../doc/hu/types/instanceof.md | 35 + .../JavaScript-Garden/doc/hu/types/typeof.md | 15 + .../doc/it/array/constructor.md | 34 + .../JavaScript-Garden/doc/it/array/general.md | 61 + .../JavaScript-Garden/doc/it/core/delete.md | 90 + .../JavaScript-Garden/doc/it/core/eval.md | 49 + .../doc/it/core/semicolon.md | 120 ++ .../doc/it/core/undefined.md | 74 + .../doc/it/function/arguments.md | 123 ++ .../doc/it/function/closures.md | 106 ++ .../doc/it/function/constructors.md | 129 ++ .../doc/it/function/general.md | 52 + .../doc/it/function/scopes.md | 241 +++ .../JavaScript-Garden/doc/it/function/this.md | 124 ++ external/JavaScript-Garden/doc/it/index.json | 69 + .../JavaScript-Garden/doc/it/intro/index.md | 51 + .../doc/it/object/forinloop.md | 68 + .../doc/it/object/general.md | 103 ++ .../doc/it/object/hasownproperty.md | 58 + .../doc/it/object/prototype.md | 126 ++ .../doc/it/other/timeouts.md | 180 ++ .../JavaScript-Garden/doc/it/types/casting.md | 75 + .../doc/it/types/equality.md | 75 + .../doc/it/types/instanceof.md | 41 + .../JavaScript-Garden/doc/it/types/typeof.md | 87 + .../doc/ja/array/constructor.md | 25 + .../JavaScript-Garden/doc/ja/array/general.md | 41 + .../JavaScript-Garden/doc/ja/core/delete.md | 75 + .../JavaScript-Garden/doc/ja/core/eval.md | 39 + .../doc/ja/core/semicolon.md | 102 ++ .../doc/ja/core/undefined.md | 53 + .../doc/ja/function/arguments.md | 98 + .../doc/ja/function/closures.md | 78 + .../doc/ja/function/constructors.md | 103 ++ .../doc/ja/function/general.md | 37 + .../doc/ja/function/scopes.md | 195 ++ .../JavaScript-Garden/doc/ja/function/this.md | 93 + external/JavaScript-Garden/doc/ja/index.json | 69 + .../JavaScript-Garden/doc/ja/intro/index.md | 38 + .../doc/ja/object/forinloop.md | 42 + .../doc/ja/object/general.md | 83 + .../doc/ja/object/hasownproperty.md | 44 + .../doc/ja/object/prototype.md | 86 + .../doc/ja/other/timeouts.md | 125 ++ .../JavaScript-Garden/doc/ja/types/casting.md | 60 + .../doc/ja/types/equality.md | 58 + .../doc/ja/types/instanceof.md | 31 + .../JavaScript-Garden/doc/ja/types/typeof.md | 71 + .../doc/ko/array/constructor.md | 24 + .../JavaScript-Garden/doc/ko/array/general.md | 40 + .../JavaScript-Garden/doc/ko/core/delete.md | 76 + .../JavaScript-Garden/doc/ko/core/eval.md | 38 + .../doc/ko/core/semicolon.md | 99 + .../doc/ko/core/undefined.md | 56 + .../doc/ko/function/arguments.md | 108 ++ .../doc/ko/function/closures.md | 95 + .../doc/ko/function/constructors.md | 104 ++ .../doc/ko/function/general.md | 37 + .../doc/ko/function/scopes.md | 191 ++ .../JavaScript-Garden/doc/ko/function/this.md | 99 + external/JavaScript-Garden/doc/ko/index.json | 69 + .../JavaScript-Garden/doc/ko/intro/index.md | 41 + .../doc/ko/object/forinloop.md | 36 + .../doc/ko/object/general.md | 82 + .../doc/ko/object/hasownproperty.md | 43 + .../doc/ko/object/prototype.md | 79 + .../doc/ko/other/timeouts.md | 123 ++ .../JavaScript-Garden/doc/ko/types/casting.md | 58 + .../doc/ko/types/equality.md | 57 + .../doc/ko/types/instanceof.md | 31 + .../JavaScript-Garden/doc/ko/types/typeof.md | 63 + external/JavaScript-Garden/doc/language.json | 4 + .../doc/pl/array/constructor.md | 37 + .../JavaScript-Garden/doc/pl/array/general.md | 58 + .../JavaScript-Garden/doc/pl/core/eval.md | 48 + .../doc/pl/core/semicolon.md | 117 ++ .../doc/pl/core/undefined.md | 69 + .../doc/pl/function/arguments.md | 121 ++ .../doc/pl/function/closures.md | 101 ++ .../doc/pl/function/constructors.md | 131 ++ .../doc/pl/function/general.md | 53 + .../doc/pl/function/scopes.md | 235 +++ .../JavaScript-Garden/doc/pl/function/this.md | 111 ++ external/JavaScript-Garden/doc/pl/index.json | 74 + .../JavaScript-Garden/doc/pl/intro/authors.md | 8 + .../doc/pl/intro/contributors.md | 4 + .../JavaScript-Garden/doc/pl/intro/hosting.md | 6 + .../JavaScript-Garden/doc/pl/intro/index.md | 13 + .../JavaScript-Garden/doc/pl/intro/license.md | 11 + .../doc/pl/intro/translators.md | 7 + .../doc/pl/object/forinloop.md | 50 + .../doc/pl/object/general.md | 102 ++ .../doc/pl/object/hasownproperty.md | 53 + .../doc/pl/object/prototype.md | 118 ++ .../doc/pl/other/timeouts.md | 158 ++ .../JavaScript-Garden/doc/pl/types/casting.md | 72 + .../doc/pl/types/equality.md | 71 + .../doc/pl/types/instanceof.md | 41 + .../JavaScript-Garden/doc/pl/types/typeof.md | 87 + .../doc/ptbr/array/constructor.md | 29 + .../doc/ptbr/array/general.md | 59 + .../JavaScript-Garden/doc/ptbr/core/delete.md | 85 + .../JavaScript-Garden/doc/ptbr/core/eval.md | 45 + .../doc/ptbr/core/semicolon.md | 100 + .../doc/ptbr/core/undefined.md | 68 + .../doc/ptbr/function/arguments.md | 110 ++ .../doc/ptbr/function/closures.md | 83 + .../doc/ptbr/function/constructors.md | 115 ++ .../doc/ptbr/function/general.md | 46 + .../doc/ptbr/function/scopes.md | 225 +++ .../doc/ptbr/function/this.md | 108 ++ .../JavaScript-Garden/doc/ptbr/index.json | 69 + .../JavaScript-Garden/doc/ptbr/intro/index.md | 38 + .../doc/ptbr/object/forinloop.md | 41 + .../doc/ptbr/object/general.md | 83 + .../doc/ptbr/object/hasownproperty.md | 56 + .../doc/ptbr/object/prototype.md | 109 ++ .../doc/ptbr/other/timeouts.md | 161 ++ .../doc/ptbr/types/casting.md | 67 + .../doc/ptbr/types/equality.md | 70 + .../doc/ptbr/types/instanceof.md | 38 + .../doc/ptbr/types/typeof.md | 83 + .../doc/ru/appendix/fromtranslators.md | 14 + .../doc/ru/array/constructor.md | 25 + .../JavaScript-Garden/doc/ru/array/general.md | 40 + .../JavaScript-Garden/doc/ru/core/delete.md | 88 + .../JavaScript-Garden/doc/ru/core/eval.md | 39 + .../doc/ru/core/semicolon.md | 101 ++ .../doc/ru/core/undefined.md | 53 + .../doc/ru/function/arguments.md | 95 + .../doc/ru/function/closures.md | 80 + .../doc/ru/function/constructors.md | 101 ++ .../doc/ru/function/general.md | 45 + .../doc/ru/function/scopes.md | 194 ++ .../JavaScript-Garden/doc/ru/function/this.md | 92 + external/JavaScript-Garden/doc/ru/index.json | 82 + .../JavaScript-Garden/doc/ru/intro/authors.md | 7 + .../doc/ru/intro/contributors.md | 4 + .../JavaScript-Garden/doc/ru/intro/index.md | 8 + .../JavaScript-Garden/doc/ru/intro/license.md | 9 + .../doc/ru/intro/translators.md | 12 + .../doc/ru/object/forinloop.md | 37 + .../doc/ru/object/general.md | 99 + .../doc/ru/object/hasownproperty.md | 40 + .../doc/ru/object/prototype.md | 87 + .../doc/ru/other/timeouts.md | 115 ++ .../JavaScript-Garden/doc/ru/types/casting.md | 61 + .../doc/ru/types/equality.md | 58 + .../doc/ru/types/instanceof.md | 32 + .../JavaScript-Garden/doc/ru/types/typeof.md | 64 + .../doc/tr/array/constructor.md | 35 + .../JavaScript-Garden/doc/tr/array/general.md | 58 + .../JavaScript-Garden/doc/tr/core/delete.md | 84 + .../JavaScript-Garden/doc/tr/core/eval.md | 48 + .../doc/tr/core/semicolon.md | 118 ++ .../doc/tr/core/undefined.md | 71 + .../doc/tr/function/arguments.md | 124 ++ .../doc/tr/function/closures.md | 100 + .../doc/tr/function/constructors.md | 124 ++ .../doc/tr/function/general.md | 49 + .../doc/tr/function/scopes.md | 232 +++ .../JavaScript-Garden/doc/tr/function/this.md | 115 ++ external/JavaScript-Garden/doc/tr/index.json | 69 + .../JavaScript-Garden/doc/tr/intro/index.md | 47 + .../doc/tr/object/forinloop.md | 49 + .../doc/tr/object/general.md | 102 ++ .../doc/tr/object/hasownproperty.md | 56 + .../doc/tr/object/prototype.md | 116 ++ .../doc/tr/other/timeouts.md | 160 ++ .../JavaScript-Garden/doc/tr/types/casting.md | 72 + .../doc/tr/types/equality.md | 75 + .../doc/tr/types/instanceof.md | 40 + .../JavaScript-Garden/doc/tr/types/typeof.md | 87 + .../doc/zh/array/constructor.md | 38 + .../JavaScript-Garden/doc/zh/array/general.md | 55 + .../JavaScript-Garden/doc/zh/core/eval.md | 67 + .../doc/zh/core/semicolon.md | 100 + .../doc/zh/core/undefined.md | 60 + .../doc/zh/function/arguments.md | 122 ++ .../doc/zh/function/closures.md | 86 + .../doc/zh/function/constructors.md | 136 ++ .../doc/zh/function/general.md | 44 + .../doc/zh/function/scopes.md | 220 +++ .../JavaScript-Garden/doc/zh/function/this.md | 102 ++ external/JavaScript-Garden/doc/zh/index.json | 72 + .../JavaScript-Garden/doc/zh/intro/authors.md | 7 + .../doc/zh/intro/contributors.md | 13 + .../JavaScript-Garden/doc/zh/intro/index.md | 14 + .../JavaScript-Garden/doc/zh/intro/license.md | 11 + .../doc/zh/object/forinloop.md | 40 + .../doc/zh/object/general.md | 93 + .../doc/zh/object/hasownproperty.md | 46 + .../doc/zh/object/prototype.md | 96 + .../doc/zh/other/timeouts.md | 132 ++ .../JavaScript-Garden/doc/zh/types/casting.md | 74 + .../doc/zh/types/equality.md | 63 + .../doc/zh/types/instanceof.md | 35 + .../JavaScript-Garden/doc/zh/types/typeof.md | 97 + .../doc/zhtw/array/constructor.md | 28 + .../doc/zhtw/array/general.md | 46 + .../JavaScript-Garden/doc/zhtw/core/delete.md | 78 + .../JavaScript-Garden/doc/zhtw/core/eval.md | 42 + .../doc/zhtw/core/semicolon.md | 103 ++ .../doc/zhtw/core/undefined.md | 53 + .../doc/zhtw/function/arguments.md | 103 ++ .../doc/zhtw/function/closures.md | 85 + .../doc/zhtw/function/constructors.md | 101 ++ .../doc/zhtw/function/general.md | 43 + .../doc/zhtw/function/scopes.md | 199 ++ .../doc/zhtw/function/this.md | 105 ++ .../JavaScript-Garden/doc/zhtw/index.json | 69 + .../JavaScript-Garden/doc/zhtw/intro/index.md | 53 + .../doc/zhtw/object/forinloop.md | 40 + .../doc/zhtw/object/general.md | 85 + .../doc/zhtw/object/hasownproperty.md | 45 + .../doc/zhtw/object/prototype.md | 87 + .../doc/zhtw/other/timeouts.md | 135 ++ .../doc/zhtw/types/casting.md | 62 + .../doc/zhtw/types/equality.md | 60 + .../doc/zhtw/types/instanceof.md | 32 + .../doc/zhtw/types/typeof.md | 74 + external/JavaScript-Garden/es/index.html | 1410 ++++++++++++++ external/JavaScript-Garden/favicon.ico | Bin 0 -> 375 bytes external/JavaScript-Garden/fi/index.html | 1018 +++++++++++ external/JavaScript-Garden/fr/index.html | 1197 ++++++++++++ external/JavaScript-Garden/hu/index.html | 1451 +++++++++++++++ .../JavaScript-Garden/image/sidebar-icon.png | Bin 0 -> 3351 bytes external/JavaScript-Garden/index.html | 1545 ++++++++++++++++ external/JavaScript-Garden/it/index.html | 1616 +++++++++++++++++ external/JavaScript-Garden/ja/index.html | 1126 ++++++++++++ .../JavaScript-Garden/javascript/garden.js | 207 +++ .../JavaScript-Garden/javascript/html5.js | 6 + .../JavaScript-Garden/javascript/prettify.js | 1 + external/JavaScript-Garden/ko/index.html | 1141 ++++++++++++ external/JavaScript-Garden/pl/index.html | 1440 +++++++++++++++ external/JavaScript-Garden/ptbr/index.html | 1365 ++++++++++++++ external/JavaScript-Garden/ru/index.html | 1062 +++++++++++ external/JavaScript-Garden/style/garden.css | 630 +++++++ external/JavaScript-Garden/style/print.css | 152 ++ external/JavaScript-Garden/tr/index.html | 1507 +++++++++++++++ external/JavaScript-Garden/zh/index.html | 1264 +++++++++++++ external/JavaScript-Garden/zhtw/index.html | 1196 ++++++++++++ 391 files changed, 48912 insertions(+) create mode 100644 external/JavaScript-Garden/CONTRIBUTING.md create mode 120000 external/JavaScript-Garden/JavaScript-Garden create mode 100644 external/JavaScript-Garden/LICENSE create mode 100644 external/JavaScript-Garden/README.md create mode 100644 external/JavaScript-Garden/by/index.html create mode 100644 external/JavaScript-Garden/doc/by/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/by/array/general.md create mode 100644 external/JavaScript-Garden/doc/by/core/delete.md create mode 100644 external/JavaScript-Garden/doc/by/core/eval.md create mode 100644 external/JavaScript-Garden/doc/by/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/by/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/by/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/by/function/closures.md create mode 100644 external/JavaScript-Garden/doc/by/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/by/function/general.md create mode 100644 external/JavaScript-Garden/doc/by/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/by/function/this.md create mode 100644 external/JavaScript-Garden/doc/by/index.json create mode 100644 external/JavaScript-Garden/doc/by/intro/index.md create mode 100644 external/JavaScript-Garden/doc/by/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/by/object/general.md create mode 100644 external/JavaScript-Garden/doc/by/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/by/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/by/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/by/types/casting.md create mode 100644 external/JavaScript-Garden/doc/by/types/equality.md create mode 100644 external/JavaScript-Garden/doc/by/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/by/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/de/index.json create mode 100644 external/JavaScript-Garden/doc/de/intro/authors.md create mode 100644 external/JavaScript-Garden/doc/de/intro/contributors.md create mode 100644 external/JavaScript-Garden/doc/de/intro/index.md create mode 100644 external/JavaScript-Garden/doc/de/intro/license.md create mode 100644 external/JavaScript-Garden/doc/en/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/en/array/general.md create mode 100644 external/JavaScript-Garden/doc/en/core/delete.md create mode 100644 external/JavaScript-Garden/doc/en/core/eval.md create mode 100644 external/JavaScript-Garden/doc/en/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/en/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/en/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/en/function/closures.md create mode 100644 external/JavaScript-Garden/doc/en/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/en/function/general.md create mode 100644 external/JavaScript-Garden/doc/en/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/en/function/this.md create mode 100644 external/JavaScript-Garden/doc/en/index.json create mode 100644 external/JavaScript-Garden/doc/en/intro/index.md create mode 100644 external/JavaScript-Garden/doc/en/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/en/object/general.md create mode 100644 external/JavaScript-Garden/doc/en/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/en/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/en/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/en/types/casting.md create mode 100644 external/JavaScript-Garden/doc/en/types/equality.md create mode 100644 external/JavaScript-Garden/doc/en/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/en/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/es/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/es/array/general.md create mode 100644 external/JavaScript-Garden/doc/es/core/delete.md create mode 100644 external/JavaScript-Garden/doc/es/core/eval.md create mode 100644 external/JavaScript-Garden/doc/es/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/es/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/es/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/es/function/closures.md create mode 100644 external/JavaScript-Garden/doc/es/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/es/function/general.md create mode 100644 external/JavaScript-Garden/doc/es/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/es/function/this.md create mode 100644 external/JavaScript-Garden/doc/es/index.json create mode 100644 external/JavaScript-Garden/doc/es/intro/index.md create mode 100644 external/JavaScript-Garden/doc/es/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/es/object/general.md create mode 100644 external/JavaScript-Garden/doc/es/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/es/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/es/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/es/types/casting.md create mode 100644 external/JavaScript-Garden/doc/es/types/equality.md create mode 100644 external/JavaScript-Garden/doc/es/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/es/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/fi/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/fi/array/general.md create mode 100644 external/JavaScript-Garden/doc/fi/core/eval.md create mode 100644 external/JavaScript-Garden/doc/fi/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/fi/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/fi/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/fi/function/closures.md create mode 100644 external/JavaScript-Garden/doc/fi/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/fi/function/general.md create mode 100644 external/JavaScript-Garden/doc/fi/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/fi/function/this.md create mode 100644 external/JavaScript-Garden/doc/fi/index.json create mode 100644 external/JavaScript-Garden/doc/fi/intro/authors.md create mode 100644 external/JavaScript-Garden/doc/fi/intro/contributors.md create mode 100644 external/JavaScript-Garden/doc/fi/intro/index.md create mode 100644 external/JavaScript-Garden/doc/fi/intro/license.md create mode 100644 external/JavaScript-Garden/doc/fi/intro/translators.md create mode 100644 external/JavaScript-Garden/doc/fi/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/fi/object/general.md create mode 100644 external/JavaScript-Garden/doc/fi/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/fi/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/fi/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/fi/types/casting.md create mode 100644 external/JavaScript-Garden/doc/fi/types/equality.md create mode 100644 external/JavaScript-Garden/doc/fi/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/fi/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/fr/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/fr/array/general.md create mode 100644 external/JavaScript-Garden/doc/fr/core/delete.md create mode 100644 external/JavaScript-Garden/doc/fr/core/eval.md create mode 100644 external/JavaScript-Garden/doc/fr/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/fr/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/fr/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/fr/function/closures.md create mode 100644 external/JavaScript-Garden/doc/fr/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/fr/function/general.md create mode 100644 external/JavaScript-Garden/doc/fr/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/fr/function/this.md create mode 100644 external/JavaScript-Garden/doc/fr/index.json create mode 100644 external/JavaScript-Garden/doc/fr/intro/index.md create mode 100644 external/JavaScript-Garden/doc/fr/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/fr/object/general.md create mode 100644 external/JavaScript-Garden/doc/fr/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/fr/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/fr/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/fr/types/casting.md create mode 100644 external/JavaScript-Garden/doc/fr/types/equality.md create mode 100644 external/JavaScript-Garden/doc/fr/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/fr/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/hu/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/hu/array/general.md create mode 100644 external/JavaScript-Garden/doc/hu/core/delete.md create mode 100644 external/JavaScript-Garden/doc/hu/core/eval.md create mode 100644 external/JavaScript-Garden/doc/hu/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/hu/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/hu/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/hu/function/closures.md create mode 100644 external/JavaScript-Garden/doc/hu/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/hu/function/general.md create mode 100644 external/JavaScript-Garden/doc/hu/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/hu/function/this.md create mode 100644 external/JavaScript-Garden/doc/hu/index.json create mode 100644 external/JavaScript-Garden/doc/hu/intro/index.md create mode 100644 external/JavaScript-Garden/doc/hu/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/hu/object/general.md create mode 100644 external/JavaScript-Garden/doc/hu/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/hu/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/hu/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/hu/types/casting.md create mode 100644 external/JavaScript-Garden/doc/hu/types/equality.md create mode 100644 external/JavaScript-Garden/doc/hu/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/hu/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/it/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/it/array/general.md create mode 100644 external/JavaScript-Garden/doc/it/core/delete.md create mode 100644 external/JavaScript-Garden/doc/it/core/eval.md create mode 100644 external/JavaScript-Garden/doc/it/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/it/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/it/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/it/function/closures.md create mode 100644 external/JavaScript-Garden/doc/it/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/it/function/general.md create mode 100644 external/JavaScript-Garden/doc/it/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/it/function/this.md create mode 100644 external/JavaScript-Garden/doc/it/index.json create mode 100644 external/JavaScript-Garden/doc/it/intro/index.md create mode 100644 external/JavaScript-Garden/doc/it/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/it/object/general.md create mode 100644 external/JavaScript-Garden/doc/it/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/it/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/it/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/it/types/casting.md create mode 100644 external/JavaScript-Garden/doc/it/types/equality.md create mode 100644 external/JavaScript-Garden/doc/it/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/it/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/ja/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/ja/array/general.md create mode 100644 external/JavaScript-Garden/doc/ja/core/delete.md create mode 100644 external/JavaScript-Garden/doc/ja/core/eval.md create mode 100644 external/JavaScript-Garden/doc/ja/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/ja/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/ja/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/ja/function/closures.md create mode 100644 external/JavaScript-Garden/doc/ja/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/ja/function/general.md create mode 100644 external/JavaScript-Garden/doc/ja/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/ja/function/this.md create mode 100644 external/JavaScript-Garden/doc/ja/index.json create mode 100644 external/JavaScript-Garden/doc/ja/intro/index.md create mode 100644 external/JavaScript-Garden/doc/ja/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/ja/object/general.md create mode 100644 external/JavaScript-Garden/doc/ja/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/ja/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/ja/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/ja/types/casting.md create mode 100644 external/JavaScript-Garden/doc/ja/types/equality.md create mode 100644 external/JavaScript-Garden/doc/ja/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/ja/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/ko/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/ko/array/general.md create mode 100644 external/JavaScript-Garden/doc/ko/core/delete.md create mode 100644 external/JavaScript-Garden/doc/ko/core/eval.md create mode 100644 external/JavaScript-Garden/doc/ko/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/ko/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/ko/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/ko/function/closures.md create mode 100644 external/JavaScript-Garden/doc/ko/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/ko/function/general.md create mode 100644 external/JavaScript-Garden/doc/ko/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/ko/function/this.md create mode 100644 external/JavaScript-Garden/doc/ko/index.json create mode 100644 external/JavaScript-Garden/doc/ko/intro/index.md create mode 100644 external/JavaScript-Garden/doc/ko/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/ko/object/general.md create mode 100644 external/JavaScript-Garden/doc/ko/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/ko/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/ko/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/ko/types/casting.md create mode 100644 external/JavaScript-Garden/doc/ko/types/equality.md create mode 100644 external/JavaScript-Garden/doc/ko/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/ko/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/language.json create mode 100644 external/JavaScript-Garden/doc/pl/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/pl/array/general.md create mode 100644 external/JavaScript-Garden/doc/pl/core/eval.md create mode 100644 external/JavaScript-Garden/doc/pl/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/pl/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/pl/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/pl/function/closures.md create mode 100644 external/JavaScript-Garden/doc/pl/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/pl/function/general.md create mode 100644 external/JavaScript-Garden/doc/pl/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/pl/function/this.md create mode 100644 external/JavaScript-Garden/doc/pl/index.json create mode 100644 external/JavaScript-Garden/doc/pl/intro/authors.md create mode 100644 external/JavaScript-Garden/doc/pl/intro/contributors.md create mode 100644 external/JavaScript-Garden/doc/pl/intro/hosting.md create mode 100644 external/JavaScript-Garden/doc/pl/intro/index.md create mode 100644 external/JavaScript-Garden/doc/pl/intro/license.md create mode 100644 external/JavaScript-Garden/doc/pl/intro/translators.md create mode 100644 external/JavaScript-Garden/doc/pl/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/pl/object/general.md create mode 100644 external/JavaScript-Garden/doc/pl/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/pl/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/pl/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/pl/types/casting.md create mode 100644 external/JavaScript-Garden/doc/pl/types/equality.md create mode 100644 external/JavaScript-Garden/doc/pl/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/pl/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/ptbr/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/ptbr/array/general.md create mode 100644 external/JavaScript-Garden/doc/ptbr/core/delete.md create mode 100644 external/JavaScript-Garden/doc/ptbr/core/eval.md create mode 100644 external/JavaScript-Garden/doc/ptbr/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/ptbr/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/closures.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/general.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/ptbr/function/this.md create mode 100644 external/JavaScript-Garden/doc/ptbr/index.json create mode 100644 external/JavaScript-Garden/doc/ptbr/intro/index.md create mode 100644 external/JavaScript-Garden/doc/ptbr/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/ptbr/object/general.md create mode 100644 external/JavaScript-Garden/doc/ptbr/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/ptbr/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/ptbr/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/ptbr/types/casting.md create mode 100644 external/JavaScript-Garden/doc/ptbr/types/equality.md create mode 100644 external/JavaScript-Garden/doc/ptbr/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/ptbr/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/ru/appendix/fromtranslators.md create mode 100644 external/JavaScript-Garden/doc/ru/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/ru/array/general.md create mode 100644 external/JavaScript-Garden/doc/ru/core/delete.md create mode 100644 external/JavaScript-Garden/doc/ru/core/eval.md create mode 100644 external/JavaScript-Garden/doc/ru/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/ru/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/ru/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/ru/function/closures.md create mode 100644 external/JavaScript-Garden/doc/ru/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/ru/function/general.md create mode 100644 external/JavaScript-Garden/doc/ru/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/ru/function/this.md create mode 100644 external/JavaScript-Garden/doc/ru/index.json create mode 100644 external/JavaScript-Garden/doc/ru/intro/authors.md create mode 100644 external/JavaScript-Garden/doc/ru/intro/contributors.md create mode 100644 external/JavaScript-Garden/doc/ru/intro/index.md create mode 100644 external/JavaScript-Garden/doc/ru/intro/license.md create mode 100644 external/JavaScript-Garden/doc/ru/intro/translators.md create mode 100644 external/JavaScript-Garden/doc/ru/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/ru/object/general.md create mode 100644 external/JavaScript-Garden/doc/ru/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/ru/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/ru/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/ru/types/casting.md create mode 100644 external/JavaScript-Garden/doc/ru/types/equality.md create mode 100644 external/JavaScript-Garden/doc/ru/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/ru/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/tr/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/tr/array/general.md create mode 100644 external/JavaScript-Garden/doc/tr/core/delete.md create mode 100644 external/JavaScript-Garden/doc/tr/core/eval.md create mode 100644 external/JavaScript-Garden/doc/tr/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/tr/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/tr/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/tr/function/closures.md create mode 100644 external/JavaScript-Garden/doc/tr/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/tr/function/general.md create mode 100644 external/JavaScript-Garden/doc/tr/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/tr/function/this.md create mode 100644 external/JavaScript-Garden/doc/tr/index.json create mode 100644 external/JavaScript-Garden/doc/tr/intro/index.md create mode 100644 external/JavaScript-Garden/doc/tr/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/tr/object/general.md create mode 100644 external/JavaScript-Garden/doc/tr/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/tr/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/tr/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/tr/types/casting.md create mode 100644 external/JavaScript-Garden/doc/tr/types/equality.md create mode 100644 external/JavaScript-Garden/doc/tr/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/tr/types/typeof.md create mode 100755 external/JavaScript-Garden/doc/zh/array/constructor.md create mode 100755 external/JavaScript-Garden/doc/zh/array/general.md create mode 100755 external/JavaScript-Garden/doc/zh/core/eval.md create mode 100755 external/JavaScript-Garden/doc/zh/core/semicolon.md create mode 100755 external/JavaScript-Garden/doc/zh/core/undefined.md create mode 100755 external/JavaScript-Garden/doc/zh/function/arguments.md create mode 100755 external/JavaScript-Garden/doc/zh/function/closures.md create mode 100755 external/JavaScript-Garden/doc/zh/function/constructors.md create mode 100755 external/JavaScript-Garden/doc/zh/function/general.md create mode 100755 external/JavaScript-Garden/doc/zh/function/scopes.md create mode 100755 external/JavaScript-Garden/doc/zh/function/this.md create mode 100755 external/JavaScript-Garden/doc/zh/index.json create mode 100755 external/JavaScript-Garden/doc/zh/intro/authors.md create mode 100755 external/JavaScript-Garden/doc/zh/intro/contributors.md create mode 100755 external/JavaScript-Garden/doc/zh/intro/index.md create mode 100755 external/JavaScript-Garden/doc/zh/intro/license.md create mode 100755 external/JavaScript-Garden/doc/zh/object/forinloop.md create mode 100755 external/JavaScript-Garden/doc/zh/object/general.md create mode 100755 external/JavaScript-Garden/doc/zh/object/hasownproperty.md create mode 100755 external/JavaScript-Garden/doc/zh/object/prototype.md create mode 100755 external/JavaScript-Garden/doc/zh/other/timeouts.md create mode 100755 external/JavaScript-Garden/doc/zh/types/casting.md create mode 100755 external/JavaScript-Garden/doc/zh/types/equality.md create mode 100755 external/JavaScript-Garden/doc/zh/types/instanceof.md create mode 100755 external/JavaScript-Garden/doc/zh/types/typeof.md create mode 100644 external/JavaScript-Garden/doc/zhtw/array/constructor.md create mode 100644 external/JavaScript-Garden/doc/zhtw/array/general.md create mode 100644 external/JavaScript-Garden/doc/zhtw/core/delete.md create mode 100644 external/JavaScript-Garden/doc/zhtw/core/eval.md create mode 100644 external/JavaScript-Garden/doc/zhtw/core/semicolon.md create mode 100644 external/JavaScript-Garden/doc/zhtw/core/undefined.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/arguments.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/closures.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/constructors.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/general.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/scopes.md create mode 100644 external/JavaScript-Garden/doc/zhtw/function/this.md create mode 100644 external/JavaScript-Garden/doc/zhtw/index.json create mode 100644 external/JavaScript-Garden/doc/zhtw/intro/index.md create mode 100644 external/JavaScript-Garden/doc/zhtw/object/forinloop.md create mode 100644 external/JavaScript-Garden/doc/zhtw/object/general.md create mode 100644 external/JavaScript-Garden/doc/zhtw/object/hasownproperty.md create mode 100644 external/JavaScript-Garden/doc/zhtw/object/prototype.md create mode 100644 external/JavaScript-Garden/doc/zhtw/other/timeouts.md create mode 100644 external/JavaScript-Garden/doc/zhtw/types/casting.md create mode 100644 external/JavaScript-Garden/doc/zhtw/types/equality.md create mode 100644 external/JavaScript-Garden/doc/zhtw/types/instanceof.md create mode 100644 external/JavaScript-Garden/doc/zhtw/types/typeof.md create mode 100644 external/JavaScript-Garden/es/index.html create mode 100644 external/JavaScript-Garden/favicon.ico create mode 100644 external/JavaScript-Garden/fi/index.html create mode 100644 external/JavaScript-Garden/fr/index.html create mode 100644 external/JavaScript-Garden/hu/index.html create mode 100644 external/JavaScript-Garden/image/sidebar-icon.png create mode 100644 external/JavaScript-Garden/index.html create mode 100644 external/JavaScript-Garden/it/index.html create mode 100644 external/JavaScript-Garden/ja/index.html create mode 100644 external/JavaScript-Garden/javascript/garden.js create mode 100644 external/JavaScript-Garden/javascript/html5.js create mode 100644 external/JavaScript-Garden/javascript/prettify.js create mode 100644 external/JavaScript-Garden/ko/index.html create mode 100644 external/JavaScript-Garden/pl/index.html create mode 100644 external/JavaScript-Garden/ptbr/index.html create mode 100644 external/JavaScript-Garden/ru/index.html create mode 100644 external/JavaScript-Garden/style/garden.css create mode 100644 external/JavaScript-Garden/style/print.css create mode 100644 external/JavaScript-Garden/tr/index.html create mode 100644 external/JavaScript-Garden/zh/index.html create mode 100644 external/JavaScript-Garden/zhtw/index.html diff --git a/external/JavaScript-Garden/CONTRIBUTING.md b/external/JavaScript-Garden/CONTRIBUTING.md new file mode 100644 index 0000000..fe3c6b6 --- /dev/null +++ b/external/JavaScript-Garden/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing to Javascript Garden + +1. We're about Javascript AKA [ECMA script](http://www.ecma-international.org/ecma-262/5.1/) not a specific implementation. So we don't cover issues specific to one environment or interpreter, like browsers or V8 for node. +1. Micro-performance is out of scope: anything that's not a result of essential algorithmic complexity (e.g O(log n) vs O(n^2)) is down to a specific version of a specific interpreter (see 1). +1. Please test code examples to ensure they work, there's nothing more confusing than incorrect examples! +1. Contributing to JS Garden makes you a fantastic person, and Brendan Eich will find you immensely attractive. + +Thank you for being kind enough to help out! + +## Testing locally + +1. Run `npm run build` to build +1. Run a webserver from the root of the repo to view + +## Getting changes merged + +1. Squash your changes into one commit +1. Make sure the site still works :) +1. Make a PR diff --git a/external/JavaScript-Garden/JavaScript-Garden b/external/JavaScript-Garden/JavaScript-Garden new file mode 120000 index 0000000..dcc6db5 --- /dev/null +++ b/external/JavaScript-Garden/JavaScript-Garden @@ -0,0 +1 @@ +./site \ No newline at end of file diff --git a/external/JavaScript-Garden/LICENSE b/external/JavaScript-Garden/LICENSE new file mode 100644 index 0000000..89d4396 --- /dev/null +++ b/external/JavaScript-Garden/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010 Ivo Wetzel. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/external/JavaScript-Garden/README.md b/external/JavaScript-Garden/README.md new file mode 100644 index 0000000..cb9cb3d --- /dev/null +++ b/external/JavaScript-Garden/README.md @@ -0,0 +1,43 @@ +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][1] on the Mozilla Developer Network. + +### The authors + +This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][6] +(Original English Language Version) and [Zhang Yi Jiang][5] (Design), and +[many others](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) +who've worked hard to provide translations or fixes. + +It's currently maintained by [Tim Ruffles](http://twitter.com/timruffles). + +## Contributing + +Please submit fixes and translations as [pull requests](https://help.github.com/articles/using-pull-requests). + +### License + +JavaScript Garden is published under the [MIT license][2] and hosted on +[GitHub][4]. If you find errors or typos please [file an issue][3] or a pull +request on the repository. You can also find us in the [JavaScript room][10] on +Stack Overflow chat. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide +[2]: https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE +[3]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[4]: https://github.com/BonsaiDen/JavaScript-Garden +[5]: http://stackoverflow.com/users/313758/yi-jiang +[6]: http://stackoverflow.com/users/170224/ivo-wetzel +[8]: https://github.com/caio +[9]: https://github.com/blixt +[10]: http://chat.stackoverflow.com/rooms/17/javascript diff --git a/external/JavaScript-Garden/by/index.html b/external/JavaScript-Garden/by/index.html new file mode 100644 index 0000000..07fb804 --- /dev/null +++ b/external/JavaScript-Garden/by/index.html @@ -0,0 +1,1525 @@ +JavaScript Garden

    Уводзіны

    Уступ

    JavaScript Garden гэта растучы набор дакументацыі аб найбольш +цікавых частках мовы праграмавання JavaScript. Ён дае парады аб тым як прадухіліць +частыя і непрадказальныя памылкі, а таксама праблемы з хуткасцю выконвання і +дрэннымі практыкамі, якія праграмісты, не з'яўляючыяся экспертамі у JavaScript +маглі сустрэць падчас сваіх пошукаў у глыбіні мовы.

    +

    JavaScript Garden не ставіць сваёй мэтай навучыць вас мове JavaScript. Былыя +веды мовы рэкамендаваныя, каб вы змаглі зразумець пытанні разглядаемыя ў гэтым +мануале. Каб зразумець базавыя рэчы мовы, калі ласка прачытайце цудоўны мануал +у сетцы распрацоўшчыкаў Mozilla.

    +

    Аўтары

    +

    Гэты мануал - праца двух выбітных карыстальнікаў Stack Overflow , Ivo Wetzel +(Тэкст) і Zhang Yi Jiang (Дызайн).

    +

    На дадзены момант падтрымліваецца Tim Ruffles.

    +

    Удзельнікі

    + +

    Хостынг

    +

    JavaScript Garden хосціцца на GitHub, але Cramer Development падтрымлівае нас +люстэркам на JavaScriptGarden.info. У Беларускамоўнай версіі таксама ёсць +сваё люстэрка на GitHub

    +

    Пераклад

    +

    Перакладзена на Беларускую мову супольнасцю it-mova.

    +

    Ліцэнзія

    +

    JavaScript Garden апублікаваны пад MIT ліцэнзіяй і хосціцца на +GitHub. Калі вы знойдзеце апячатку або памылку - пазначце памылку або +адпраўце pull request у сховішча. Вы таксама можаце знайсці нас у +JavaScript room на чаце Stack Overflow.

    +

    Аб'екты

    Выкарыстанне і ўласцівасці аб'ектаў

    Усё ў JavaScript дзейнічае як аб'ект, апроч двух выключэнняў — гэта +null і undefined.

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

    Часта распрацоўшчыкі думаюць, што лічбавыя літэралы не могуць быць выкарыстаны як +аб'екты. Гэта праз тое, што сінтаксічны аналізатар JavaScript стараецца прывесці +натацыю кропка пасля нумара да літэрала з плаваючай кропкай.

    +
    2.toString(); // уздымае SyntaxError
    +

    Ёсць некалькі падыходаў, якія могуць дазволіць выкарыстаць лікавыя літэралы як +аб'екты'.

    +
    2..toString(); // другая кропка распазнаецца слушна
    +2 .toString(); // заўважце прабел з лева ад кропкі
    +(2).toString(); // 2 распазнаецца першым чынам
    +

    Аб'ект як тып дадзеных

    +

    Аб'екты ў JavaScript таксама могуць быць выкарыстаныя як хэш-табліцы; яны ў асноўным +складаюцца з іменаваных уласцівасцяў з адпаведнымі значэннямі.

    +

    Выкарыстоўваючы натацыю літэрала аб'екта — {} — магчыма стварыць +просты аб'ект. Гэты новы аб'ект пашырае Object.prototype і +не мае сваіх уласцівасцяў якія былі б вызначыныя.

    +
    var foo = {}; // новы пусты аб'ект
    +
    +// новы аб'ект з уласціваццю 'test', якая мае значэнне 12
    +var bar = {test: 12};
    +

    Доступ да ўласцівасцяў

    +

    Доступ да ўласцівасцяў аб'екта можа быць здейснены двумя спосабамі, праз кропкавую +натацыю або натацыю з квадратнымі дужкамі.

    +
    var foo = {name: 'кацяня'}
    +foo.name; // кацяня
    +foo['name']; // кацяня
    +
    +var get = 'name';
    +foo[get]; // кацяня
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // працуе
    +

    Натацыі працуюць амаль што ідэнтычна, з адзінай розніцай у тым, што +натацыя з квадратнымі дужкамі дазваляе дынамічную устаноўку ўласцівасцяў і +выкарыстанне імёнаў уласцівасцяў, якія інакш прывялі б да сінтаксічных памылак.

    +

    Выдаленне ўласцівасцяў

    +

    Адзіны спосаб выдаліць уласціваць з аб'екта — гэта выкарыстаць аператар delete; +пазначэнне уласціваці як undefined або null толькі прыбірае +значэнне звязанае з уласцівацю, але не ключ.

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

    Вышэй прыведзены код вывядзе bar undefined і foo null — толькі baz быў +выдалены і таму адсутнічае ў вывадзе.

    +

    Натацыя ключэй

    +
    var test = {
    +    'case': 'Я ключавое слова, таму я павінна быць пазначана як радок',
    +    delete: 'Я таксама ключавое слова, таму і я' // уздымае SyntaxError
    +};
    +

    Уласцівасці аб'ектаў могуць быць пазначаныя як сімваламі, так і ў выглядзе радкоў. +Праз яшчэ адну хібу сінтаксічнага аналізатара JavaScript, вышэй прыведзены код +кіне SyntaxError у весіях ранейшых за ECMAScript 5.

    +

    Гэта памылка ўздымаецца праз тое, што delete - гэта ключавое слова; такім чынам, +яно мае быць пазначана як літэрал радка каб забяспечыць, што яно будзе какрэктна +інтэрпрэтавана старымі рухавікамі JavaScript.

    +

    Прататып

    JavaScript не прадастаўляе класічную мадэль спадкаемства; замест гэтага, ён +выкарыстоўвае прататыпную мадэль.

    +

    Негледзячы на тое, што гэта лічыцца адной з слабасцяў JavaScript, мадэль +прататыпнага спадкаемства больш эфэктыўная за класічную. +Напрыклад, даволі трывіальна пабудаваць класічную мадэль паверх прататыпнай мадэлі, +у той час як адваротнае было б значна больш складаным.

    +

    JavaScript гэта адзіная шырока выкарыстоўваемая мова, якая падтрымлівае +прататыпнае спадкаемства, таму можа спатрэбіцца час, каб прызвычаіцца да гэтай мадэлі.

    +

    Першая вялікая розніца заключаецца ў тым, што JavaScript выкастроўвае прататыпныя +ланужкі.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Пазначае прататыпам Bar новы асобнік Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Упэнімся, што Bar з'яўляецца дзейсным канструктарам
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // стварае новы асобнік Bar
    +
    +// Выніковы ланцужок прататыпаў
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +          { toString: ... /* і г.д. */ }
    +

    У вышэй прыведзеным кодзе аб'ект test атрымае спадчыну і ад Bar.prototype, +і ад Foo.prototype; такім чынам, ён будзе мець доступ да функцыі method, якая +вызначана ў Foo. А таксама доступ да ўласцівасці value +аднаго унікальнага асобніка Foo, які з'яўляецца яго прататыпам. Важна заўважыць, +што new Bar() не стварае новы асобнік Foo, але выкарыстоўвае функцыю, +пазначаную яго прататыпам; такім чынам, усе асобнікі Bar будуць выкарыстоўваць +тую ж уласціваць value.

    + +

    Пошук уласцівасцяў

    +

    Калі адбываецца зварот да ўласцівасці, JavaScript пройдзе па ўсім +ланцужку прататыпаў уверх да таго моманту, як знойдзе ўласціваць з запытаным імем.

    +

    У той момант, калі дасягнуты верх ланцужка - а менавіта Object.prototype - і ўсё яшчэ +не знойдзена адпаведная ўласцівасць, будзе вернута значэнне undefined.

    +

    Уласцівасць prototype

    +

    Нягледзячы на тое, што ўласцівасць prototype выкарыстоўваецца мовай, каб пабудаваць +ланцужок прататыпаў, магчыма прызначыць яму любое значэнне. Аднак, прызначэнне +прымітываў будузе праігнараваным.

    +
    function Foo() {}
    +Foo.prototype = 1; // без эфекту
    +

    Прызначэнне аб'ектаў, як паказана ў прыкладзе вышэй, будзе працаваць, і дазволіць +дынамічна ствараць ланцужкі прататыпаў.

    +

    Хуткасць выканання

    +

    Пошук уласцівасцяў, якія знаходзяцца высока ў ланцужку прататыпаў, можа +негатыўна адбіцца на хуткасці выканання, і гэта можа быць прыкметным у кодзе, у якім +чыннік хуткасці крытычны. У выпадку спробы доступа да неіснуючых уласцівасцяў +будзе пройдзены ўвесь ланцужок прататыпаў.

    +

    У дадатак, пры ітэрацыі па ўласцівасцях аб'екта +кожная уласціваць, што ёсць у ланцужку прататыпаў будзе апрацавана.

    +

    Расшырэнне ўбудаваных прататыпаў

    +

    Адна з дрэнных магчымасцяў, што сустракаецца даволі часта — расшырэнне прататыпа +Object.prototype або аднаго з іншых убудаваных тыпаў.

    +

    Такая практыка называецца monkey patching і парушае інкапсуляцыю. Хаця +папулярныя фрэймворкі, такія як Prototype шырока выкарыстоўваюць гэтую +мачымасць, няма добрых матываў для нагрувашчвання ўбудаваных тыпаў дадатковай +нестандартнай функцыянальнасцю.

    +

    Адзіным добрым матывам расшырэння убудаваных прататыпаў — гэта дадаванне функцыянала, +што з'явіўся у новых рухавіках JavaScript; напрыклад, Array.forEach.

    +

    У завяршэнне

    +

    Вельмі важна разумець, як працуе мадэль прататыпнага спадкаемства да таго, як +пісаць код, які яе выкарыстоўвае. Таксама сачыце за даўжынёй ланцужка прататыпаў +і драбіце іх, калі ёсць магчымасць, каб пазбегнуць праблем з прадукцыйнасцю. +Таксама ўбудаваныя прататыпы ніколі не павінны расшырацца, акрамя як для +таго, каб падтрымаць новыя магчымасці JavaScript.

    +

    Метад hasOwnProperty

    Каб праверыць, ці ёсць у аб'екта ўласцівасць, вызначаная ў ім самім, а не дзе-небудзь +у яго ланцужку прататыпаў, неабходна выкарыстаць метад +hasOwnProperty, які ўсе аб'екты ўспадкоўваюць ад Object.prototype.

    + +

    hasOwnProperty — адзіная функцыя ў JavaScript, якая дазваляе атрымаць уласцівасці +аб'екта без зварота да ланцужка прататыпаў.

    +
    // Сапсуем 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
    +

    Толькі hasOwnProperty дасць правільны чаканы вынік. Паглядзіце секцыю аб +цыкле for in для падрабязнейшых звестак аб тым, як +выкарыстоўваць hasOwnProperty падчас ітэрацыі па ўласцівасцях аб'екта.

    +

    hasOwnProperty як уласцівасць

    +

    JavaScript не абараняе ўласцівасць hasOwnProperty; такім чынам, ёсць верагоднасць +што ў аб'екта можа быць уласцівасць з такім імем, неабходна выкарыстаць +знешні hasOwnProperty для карэктнага выніку.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Тут жывуць драконы'
    +};
    +
    +foo.hasOwnProperty('bar'); // заўсёды верне false
    +
    +// выкарыстайце hasOwnProperty іншага аб'екта
    +// і перадайце foo у якасці this
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    +// Такасама магчыма выкарыстаць hasOwnProperty з Object.prototype
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
    +

    Заключэнне

    +

    Выкарыстоўванне hasOwnProperty ёсць адзіным надзейным спосабам, каб +праверыць існаванне ўласцівасці ў аб'екце. Рэкамендуецца выкарыстоўваць +hasOwnProperty пры ітэрацыі па ўласцівасцях аб'екта, як апісана ў секцыі +цыкла for in .

    +

    Цыкл for in

    Як і аператар in, цыкл for in праходзіць па ўсім ланцужку прататыпаў +пры ітэрацыі па ўласцівасцях аб'екта.

    + +
    // Атруцім Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // вывядзе і bar і moo
    +}
    +

    Праз тое, што немагчыма памяняць паводзіны самаго цыкла for in, неабходна фільтраваць +непажаданыя ўласцівасці аб'екта ўнутры цыкла. У версіях ECMAScript 3 і пазней, +гэта можна зрабіць праз метад hasOwnProperty.

    +

    Пачынаючы з ECMAScript 5, Object.defineProperty можа быць выкарыстана з +enumerable роўным false, каб дадаць уласцівасці аб'екту такім чынам, што яны +не будуць пералічаны. У такім выпадку было б справядлівым меркаваць, што любая +enumerable уласціваць была дададзена адмыслова і прапусціць hasOwnProperty, бо +гэта робіць код больш шматслоўным і цяжэйшым для чытання. У бібліятэчным кодзе +hasOwnProperty мае быць усё роўна выкарыстаны, бо не варта рабіць здагадкі аб +тым, якія enumerable уласцівасці могуць пражываць у ланцужку прататыпаў.

    + +

    Выкарыстоўванне hasOwnProperty для фільтрацыі

    +
    // возьмем foo з прыкладу
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Гэта адзіная правільная версія выкарыстоўвання цыкла. Дзякуючы выкарыстоўванню +hasOwnProperty, быдзе выведзена толькі moo. +Калі прыбраць hasOwnProperty, код будзе схільны да памылак у выпадку, калі +натыўныя прататыпы — напрыклад, Object.prototype — былі змененыя.

    +

    У новых версіях ECMAScript уласцівасці, пазначаныя як не enumerable, могуць быць +вызначыныя праз Object.defineProperty, змяншаючы рызыку ітэрацыі праз іх без +выкарыстання hasOwnProperty. Тым не менш, трэба быць уважлівым пры выкарыстанні +старых бібліятэк, такіх як Prototype, якая не выкарыстроўвае новымя магчымасці +ECMAScript. +Пры выкарыстоўванні гэтай бібліятэкі, цыклы for in, якія не выкарыстоўваюць +hasOwnProperty, гарантавана не будуць працаваць.

    +

    У заключэнне

    +

    Рэкамендавана заўсёды выкарыстоўваць hasOwnProperty як у ECMAScript 3 або +ніжэй, так і ў бібліятэчным кодзе. У гэтых асяродках ніколі не варта рабіць +здагадкі аб тым, быў зменены натыўны прататып ці не. Пачынаючы з ECMAScript 5, +Object.defineProperty дазваляе пазначаць уласцівасці як не enumerable і прапускаць +выкарыстоўванне hasOwnProperty у кодзе праграмы.

    +

    Функцыі

    Выразы і аб'яўленне функцый

    У JavaScript функцыі таксама з'яўляюцца аб'ектамі. Гэта значыць іх можна перадаваць +і прысвойваць як і любыя іншыя аб'екты. Адзін, часта выкарыстоўваемы варыянт, +гэтай магчымасці - перадача ананімнага метада як функцыі зваротнага выкліку +іншай, магчыма асінхроннай функцыі.

    +

    Аб'яўленне function

    +
    function foo() {}
    +

    У вышэй прыведзеным прыкладзе функцыя уздымаецца перад тым +як пачынаецца выконванне праграмы; Такім чынам, яна даступная паўсюль у зоне +бачнасці, у якой яна была аб'яўлена, нават калі выклік адбываецца да фактычнага +аб'яўлення ў кодзе.

    +
    foo(); // Працуе, бо функцыя будзе створана да выконвання кода
    +function foo() {}
    +

    function як выраз

    +
    var foo = function() {};
    +

    У гэтым прыкладзе пераменнай foo прысвойваецца ананімная функцыя.

    +
    foo; // 'undefined'
    +foo(); // уздыме TypeError
    +var foo = function() {};
    +

    Праз тое, што var - гэта аб'яўленне якое уздымае імя пераменнай foo перад тым +як код будзе выкананы, foo будзе ўжо аб'яўленым калі ён пачне выконвацца.

    +

    Але так як прысвойванні адбываюцца толькі пад час выконвання, значэнне foo +будзе змоўчанным (undefined) да выконвання адпаведнага кода.

    +

    Выразы з іменаванымі функцыямі

    +

    Яшчэ адзін выбітны выпадак - прысвойванне іменавай функцыі.

    +
    var foo = function bar() {
    +    bar(); // працуе
    +}
    +bar(); // ReferenceError
    +

    Тут, bar не даступны ў знешнім скоўпе, бо функцыя толькі прысвойваецца пераменнай +foo; аднак, унутры bar, імя даступнае. Так адбываецца праз асаблівасці працы +з прастранствамі імён у JavaScript - імя функцыі заўсёды +даступнае ў лакальным скоўпе функцыі.

    +

    Як працуе this

    У JavaScript this азначае канцэптуальна іншую рэч, чым у іншых мовах праграмавання. +Існуе роўна пяць варыянтаў таго, да чаго можа быць прывязана this.

    +

    Глабальна зона бачнасці

    +
    this;
    +

    Калі this выкарыстоўваецца ў глабальнай зоне бачнасці - яна спасылаецца на глабальны аб'ект

    +

    Выклік функцыі

    +
    foo();
    +

    Тут, this усё яшчэ спасылаецца на глабальны аб'ект.

    + +

    Выклік метада

    +
    test.foo();
    +

    У гэтым выпадку, this будзе спасылацца на аб'ект test.

    +

    Выклік канструктара

    +
    new foo();
    +

    Выклік функцыі, перад якім прысутнічае ключавое слова new, выступае ў якасці +канструктара. Унутры функцыі, this будзе спасылацца +на новаствораны аб'ект.

    +

    Яўная ўстаноўка this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // масіў разгорнецца ў ніжэйпрыведзенае
    +foo.call(bar, 1, 2, 3); // вынікам будзе a = 1, b = 2, c = 3
    +

    Пры выкарыстоўванні метадаў call або apply з Function.prototype, значэнню +this унутры выкліканай функцыі яўна прысвойваецца значэнне першага +аргумента адпаведнага выкліка функцыі.

    +

    Як вынік, у вышэйпрыведзеным прыкладзе правіла метада не працуе, і this +унутры foo будзе мець значэнне bar.

    + +

    Магчымыя пасткі

    +

    Не гледзячы на тое, што большасць гэтых прыкладаў лагічныя, першы можна лічыць +яшчэ адным недаглядам мовы, бо ён ніколі не мае практычнага прымянення.

    +
    Foo.method = function() {
    +    function test() {
    +        // this спасылаецца на глабальны аб'ект
    +    }
    +    test();
    +};
    +

    Памылковым меркаваннем будзе тое, што this унутры test будзе спасылацца на +Foo; Але на самрэч гэта не так.

    +

    Каб атрымаць доступ да Foo з цела test, вы можаце стварыць лакальную +пераменную унутры метада што будзе спасылацца на Foo.

    +
    Foo.method = function() {
    +    var self = this;
    +    function test() {
    +        // Тут выкарыстоўвайце self замест this
    +    }
    +    test();
    +};
    +

    self гэта звычайнае імя пераменнай, але яно часта выкарыстоўваецца для спасылкі +на знешні this. У камбінацыі з замыканнямі, яно можа быць +выкарыстана для перадачы this навокал.

    +

    У ECMAScript 5 можна выкарыстаць метад bind у камбінацыі з ананімнай функцыяй, +дзеля таго каб атрымаць аналагічны вынік.

    +
    Foo.method = function() {
    +    var test = function() {
    +        // this цяпер спасылаецца на Foo
    +    }.bind(this);
    +    test();
    +};
    +

    Прысвойванне метадаў

    +

    Яшчэ адна рэч якая не працуе ў JavaScript - гэта стварэнне псэўданімаў функцый, +то бок прысвойванне значэння метада пераменнай.

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

    Паводле першага правіла, test цяпер працуе як звычайны выклік функцыі; +адпаведна, this унутры больш не будзе спасылацца на someObject.

    +

    Поздняе звязванне this можа падацца дрэннай ідэяй, але насамрэч якраз дзякуючы +гэтаму працуе спадкаемства прататыпаў.

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

    Калі выклікаецца method новага экзэмпляра Bar, this будзе спасылацца на гэты +экзэмпляр.

    +

    Замыканні і спасылкі

    Адна з найбольш магутных магчымасцяў JavaScript — магчымасць ствараць замыканні. +Зона бачнасці замыканняў заўсёды мае доступ да знешняй зоны бачнасці, у якой +замыканне было аб'яўлена. З той прычыны, што ў JavaScript адзіны механізм працы +з зонай бачнасці — гэта зоны бачнасці функцыі, усе функцыі +выступаюць у якасці замыканняў.

    +

    Эмуляцыя прыватных пераменных

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

    Тут Counter вяртае два замыканні: функцыю increment і функцыю get. +Абедзьве функцыі маюць спасылку на зону бачнасці Counter і таму заўсёды +маюць доступ да пераменнай count, што была аб'яўлена ў гэтай зоне бачнасці.

    +

    Якім чынам гэта працуе

    +

    З той прычыны, што ў JavaScript немагчыма спасылацца або прысвойваць зоны бачнасці, +немагчыма атрымаць доступ да пераменнай count звонку. Адзіны спосаб +узаемадзейнічаць з ім — выкарыстоўваць два замыканні.

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

    Вышэйпрыведзены код не памяняе значэнне пераменнай count у зоне бачнасці +Counter, бо foo.hack не быў аб'яўлены у гэтай зоне бачнасці. Замест гэтага +ён створыць або перазапіша глабальную пераменную count.

    +

    Замыканні ўнутры цыклаў

    +

    Частая памылка - выкарыстанне замыканняў унутры цыклаў, як быццам бы яны капіруюць +значэнне пераменнай індэксу цыкла.

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

    Вышэйпрыведзены код не выведзе нумары ад 0 да 9, ён проста выведзе +нумар 10 дзесяць разоў.

    +

    Ананімная функцыя захоўвае спасылку на i. У той час, калі функцыя console.log +выклікаецца, цыкл for ужо адпрацаваў, а значэнне i ўжо стала 10.

    +

    Каб атрымаць пажаданыя паводзіны, неабходна стварыць копію значэння i.

    +

    Як абыйсці праблемы спасылкі

    +

    Каб стварыць копію значэння пераменнай індэкса цыкла, лепшы спосаб — стварэнне +ананімнай абгорткі.

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

    Знешняя ананімная функцыя выконваецца імгненна з i ў якасці першага аргумента +і атрымае копію значэння i ў якасці параметра e.

    +

    Ананімная функцыя, што перадаецца метаду setTimeout, цяпер мае спасылку на +e, чыё значэнне не мяняецца на працягу цыкла.

    +

    Яшчэ адзін спосаб атрымаць такі вынік — вяртаць функцыю з ананімнай абгорткі, +што будзе паводзіць сябе такім жа чынам, як і папярэдні прыклад.

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

    Яшчэ адзін папулярны спосаб дасягнуць гэтага — дадаць яшчэ адзін аргумент выкліку +функцыі setTimeout, якая перадасць агрумент функцыі зваротнага выкліку.

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

    Некаторыя старыя асяродкі JS (Internet Explorer 9 і ніжэй) не падтрымліваюць +гэтую магчымасць.

    +

    Таксама магчыма выканаць гэта выкарыстоўваючы .bind, якая можа звязаць +this і аргументы функцыі. Ніжэйпрыведзены прыклад працуе як папярэднія.

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

    Аб'ект arguments

    У зоне бачнасці любой функцыі JavaScript ёсць доступ да адмысловай пераменнай +arguments. Гэтая пераменная утрымлівае спіс усіх аргументаў, што былі +перададзеныя функцыі.

    + +

    Аб'ект arguments не з'яўляецца спадкаемцам Array. Ён мае падабенствы +з масівам, напрыклад уласцівасць length. Але ён не ўспадкоўвае Array.prototype, +а ўяўляе з сябе Object.

    +

    Таму немагчыма выклікаць стандартныя метады push, pop або slice +у аб'екта arguments. Тым не менш, ітэрацыя з звычайным цыклам for працуе карэктна. +Неабходна канвертаваць яго ў сапраўдны аб'ект Array, каб прымяніць стандартныя +метады масіваў.

    +

    Канвертацыя ў масіў

    +

    Ніжэйпрыведезны код верне новы масіў, які будзе ўтрымліваць усе элементы аб'екта +arguments.

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

    Такая канвертацыя марудная, яе не рэкамендуецца выкарыстоўваць у крытычных +у плане прадукцыйнасці частках кода.

    +

    Перадача arguments

    +

    Ніжэй прадстаўлены рэкамендаваны спосаб перадачы аргументаў з адной функцыі ў іншую.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // тут робім што-небудзь
    +}
    +

    Яшчэ адзін прыём — гэта выкарыстанне call і apply разам, каб ператварыць метады, +што выкарыстоўваюць значэнне this як і свае аргументы, у звычайныя функцыі, +што выкарыстоўваюць толькі аргументы.

    +
    function Person(first, last) {
    +  this.first = first;
    +  this.last = last;
    +}
    +
    +Person.prototype.fullname = function(joiner, options) {
    +  options = options || { order: "western" };
    +  var first = options.order === "western" ? this.first : this.last;
    +  var last =  options.order === "western" ? this.last  : this.first;
    +  return first + (joiner || " ") + last;
    +};
    +
    +// Ствараем незвязаную версію "fullname", што можа быць выкарыстана з любым
    +// аб'ектам, які мае ўласцівасці 'first' і 'last', перададзеным у якасці
    +// першага параметра. Гэтую абгортку не трэба будзе мяняць, калі колькасць або
    +// парадак аргументаў fullname зменяцца.
    +Person.fullname = function() {
    +  // Result: Person.prototype.fullname.call(this, joiner, ..., argN);
    +  return Function.call.apply(Person.prototype.fullname, arguments);
    +};
    +
    +var grace = new Person("Grace", "Hopper");
    +
    +// 'Grace Hopper'
    +grace.fullname();
    +
    +// 'Turing, Alan'
    +Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
    +

    Фармальныя параметры і індэксы аргументаў

    +

    Аб'ект arguments стварае гэтэр і сэтэр як да кожнай са сваіх уласцівасцяў, +так і да фармальных параметраў функцыі.

    +

    У выніку змена значэння фармальнага параметра зменіць таксама адпаведную ўласцівасць +аб'екта arguments, і наадварот.

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

    Міфы і праўда аб прадукцыйнасці

    +

    Адзінае, калі arguments не ствараецца, — гэта калі ёсць фармальны аргумент функцыі +або пераменная ўнутры яе з такім іменем. Не важна, выкарыстоўваюцца яны ці не.

    +

    Як гэтэры, так і сэтэры ствараюцца заўсёды, таму іх выкарыстоўванне не +мае амаль ніякага ўплыву на прадукцыйнасць.

    + +

    Тым не менш, ёсць адна рэч, якая можа жахліва знізіць прадукцыйнасць у сучасных +рухавіках JavaScript — гэта выкарыстанне arguments.callee.

    +
    function foo() {
    +    arguments.callee; // робім што-небудзь з функцыяй foo
    +    arguments.callee.caller; // і з функцыяй, якая выклікала foo
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Звычайна ўстаўляецца...
    +    }
    +}
    +

    У вышэйпрыведзеным кодзе foo больш не можа быць устаўлена, бо ёй трэба +ведаць аб сабе і аб функцыі, што яе выклікала. Гэта не толькі знішчае павышэнне +прадукцыйнасці, якое магло адбыцца дзякуючы ўстаўцы, але і парушае інкапсуляцыю, бо +функцыя цяпер залежыць ад спецыфічнага кантэксту, які яе выклікае.

    +

    Выкарыстоўванне arguments.callee або яго ўласцівасцяў вельмі непажадана.

    + +

    Канструктары

    Канструктары ў JavaScript таксама адрозніваюцца ад большасці іншых моваў. Любы +выклік функцыі, якому папярэднічае ключавое слова new з'яўляецца канструктарам.

    +

    Унутры канструктара (выкліканай функцыі) - значэнне this спасылаецца на +новаствораны аб'ект. Прататыпам новага +аб'екта прызначаецца prototype функцыі, што была выклікана ў якасці канструктара.

    +

    У выпадку, калі выкліканая функцыя не вяртае яўнага значэння праз return, будзе +не яўна вернута значэнне this, то бок новы аб'ект.

    +
    function Person(name) {
    +    this.name = name;
    +}
    +
    +Person.prototype.logName = function() {
    +    console.log(this.name);
    +};
    +
    +var sean = new Person();
    +

    У гэтым прыкладзе Person выклікаецца ў якасці канструктара, адпаведна prototype +створанага аб'екта будзе прывязаны да Person.prototype.

    +

    Вярнуць яўнае значэнне праз return, можна толькі калі гэта значэнне - Object.

    +
    function Car() {
    +    return 'ford';
    +}
    +new Car(); // новы аб'ект, не 'ford'
    +
    +function Person() {
    +    this.someValue = 2;
    +
    +    return {
    +        name: 'Charles'
    +    };
    +}
    +new Person(); // вяртае аб'ект ({name:'Charles'}), які не ўтрымлівае someValue
    +

    Калі ключавое слова new прапушчана, функцыя не верне аб'ект.

    +
    function Pirate() {
    +    // пазначыць значэнне ў глабальным аб'екце!
    +    this.hasEyePatch = true;
    +}
    +var somePirate = Pirate(); // somePirate == undefined
    +

    Гэты прыклад можа спрацаваць у некаторых выпадках, праз тое як працуе +this у JavaScript. Значэннем this тут будзе +глабальны аб'ект.

    +

    Фабрыкі

    +

    Каб мець магчымасць прапусціць ключавое слова new, канструктар функцыі мае яўна +вяртаць значэнне.

    +
    function Robot() {
    +    var color = 'gray';
    +    return {
    +        getColor: function() {
    +            return color;
    +        }
    +    }
    +}
    +
    +new Robot();
    +Robot();
    +

    Абодва выклікі Robot вернуць тое ж самае, новы аб'ект, які мае ўласцівасць +getColor, што з'яўляецца замыканнем.

    +

    Таксама варта адзначыць, што выклік new Robot() не ўплывае на прататып +вернутага аб'екта. Хаця прататып будзе прызначаны новастворанаму аб'екту, Robot +ніколі не верне гэты аб'ект.

    +

    У прыкладзе вышэй, няма розніцы паміж выклікам функцыі з аператарам new або +без яго.

    +

    Стварэнне новых аб'ектаў з выкарыстаннем фабрык

    +

    Часта рэкамендуюць не выкарыстоўваць new бо забыўшыся выкарыстаць яго, можна +стварыць памылку.

    +

    Каб стварыць новы аб'ект, лепш выкарыстоўваць фабрыку і стварыць новы аб'ект +унутры фабрыкі.

    +
    function CarFactory() {
    +    var car = {};
    +    car.owner = 'nobody';
    +
    +    var milesPerGallon = 2;
    +
    +    car.setOwner = function(newOwner) {
    +        this.owner = newOwner;
    +    }
    +
    +    car.getMPG = function() {
    +        return milesPerGallon;
    +    }
    +
    +    return car;
    +}
    +

    Хоць гэты прыклад і спрацуе негледзячы на забытае new, і бясспрэчна выкарыстоўвае +прыватныя пераменныя, ён мае некалькі недахопаў.

    +
      +
    1. Ён выкарыстоўвае больш памяці, бо функцыі створаных аб'ектаў не захоўваюццца +у прататыпе, а ствараюцца на нова для кожнага аб'екта.
    2. +
    3. Каб эмуляваць спадкаемства, фабрыка мае скапіраваць метады іншага аб'екта, або +пазначыць прататыпам новага аб'екта стары.
    4. +
    5. Разрыў ланцужка прататыпаў, проста па прычыне забытага ключавога слова new, +не адпавядае духу мовы JavaScript.
    6. +
    +

    У заключэнне

    +

    Негледзячы на тое, што прапушчанае new можа выліцца ў памылку, гэта не +прычына адмовіцца ад выкарыстання прататыпаў. У выніку лепш высвятліць якое рашэнне +больш адпавядае патрабаванням праграмы. Асабліва важна выбраць пэўны стыль і +паслядоўна выкарыстоўваць яго.

    +

    Зоны бачнасці і прасторы імёнаў

    Негледзячы на тое, што JavaScript добра працуе з сінтаксісам фігурных дужак для +блокаў, у ім няма падтрымкі блочнай зоны бачнасці; усё што ёсць на гэты конт +у мове - зона бачнасці функцыі.

    +
    function test() { // зона бачнасці
    +    for(var i = 0; i < 10; i++) { // не зона бачнасці
    +        // лічым
    +    }
    +    console.log(i); // 10
    +}
    + +

    Таксама JavaScript не падтрымлівае выразныя прасторы імёнаў, усё аб'яўляецца ў +агульнадаступнай прасторы імёнаў.

    +

    Для кожнай спасылкі на пераменную, JavaScript пойдзе ўверх па ўсіх зонах бачнасці, +пакуль не знойдзе яе. У выпадку, калі ён дойдзе да глабальнай прасторы імён і ўсё +яшчэ не знойдзе неабходнае імя, ён уздыме ReferenceError.

    +

    Атрута глабальнымі пераменнымі

    +
    // скрыпт A
    +foo = '42';
    +
    +// скрыпт B
    +var foo = '42'
    +

    Вышэйпрыведзеныя скрыпты маюць розныя вынікі. Скрыпт A аб'яўляе пераменную +foo у глабальнай зоне бачнасці, скрыпт B аб'яўляе foo у актуальнай зоне +бачнасці.

    +

    Паўторымся, гэта абсалютна не той жа самы вынік: не выкарыстоўваенне var +можа мець сур'ёзныя наступствы.

    +
    // глабальная зона бачнасці
    +var foo = 42;
    +function test() {
    +    // лакальная зона бачнасці
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    З-за таго, што аператар var прапушчаны ўнутры функцыі test, значэнне foo +у глабальнай прасторы імён будзе перазапісаным. Хаця першапачаткова гэта можа +падацца невялікай праблемай, не выкарыстоўванне var у кодзе на тысячы радкоў, +прывядзе да жахлівых, цяжкіх для адладкі памылак.

    +
    // глабальная прастора імёнаў
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // прастора імёнаў subLoop
    +    for(i = 0; i < 10; i++) { // аператар var прапушчаны
    +        // робім чароўныя рэчы!
    +    }
    +}
    +

    Знешні цыкл скончыцца пасля першага выкліка subLoop, бо subLoop перазапісвае +глабальную пераменную i. Выкарыстоўваючы var для другога цыкла for можна +было б пазбегнуць памылкі. Ніколі не прапускайце аператар var, акрамя +выпадкаў, калі змена дадзеных у знешняй зоне бачнасці ёсць пажаданым вынікам.

    +

    Лакальныя пераменныя

    +

    Адзіная крыніца лакальных пераменных у JavaScript гэта параметры функцыі +і пераменныя аб'яўленыя праз аператар var.

    +
    // глабальная зона бачнасці
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // лакальная зона бачнасці функцыі test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    foo і i гэта лакальныя пераменныя унутры зоны бачнасці функцыі test, +а вось прызначэнне bar перазапіша глабальныю пераменную з тым жа іменем.

    +

    Падыманне

    +

    JavaScript падымае аб'яўленні. Гэта азначае, што абодва аб'яўленні аператараў +var і function падымуцца на верх іх зоны бачнасці.

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

    Вышэйпрыведзены код трансфармуецца перад пачаткам выконвання. JavaScript падымае +аператары var, як і аб'яўленне function, наверх бліжэйшай зоны бачнасці.

    +
    // аператар var перамяшчаецца сюды
    +var bar, someValue; // па змоўчванню - 'undefined'
    +
    +// аб'яўленне функцыі таксама падымаецца наверх
    +function test(data) {
    +    var goo, i, e; // адсутная блочная зона бачнасці перайшла сюды
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // падае з TypeError бо ўсё яшчэ 'undefined'
    +someValue = 42; // прысвойванні не падымаюцца
    +bar = function() {};
    +
    +test();
    +

    Адсутнасць блочнай зоны бачнасці не толькі падыме аператар var па-за межы цыкла +і яго цела, але таскама зробіць вынік некаторых канструкцый if не-інтуітыўным.

    +

    Хоць у арыгінальным кодзе падаецца што канструкцыя if змяняе глабальную +пераменную goo, на дадзены момант гэта мяняе лакальную пераменную - пасля +таго, як было прыменена падыманне.

    +

    Без ведаў аб падыманні, можна падумаць што код ніжэй кіне ReferenceError.

    +
    // правярае ці было SomeImportantThing праініцыалізавана
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Але канешне, гэта працуе праз тое, што аператар var быў падняты на верх +глабальнай зоны бачнасці.

    +
    var SomeImportantThing;
    +
    +// тут нейкі код можа ініцыалізаваць SomeImportantThing, або не
    +
    +// тут у гэтым можна ўпэўніцца
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Парадак доступу да пераменных

    +

    Усе зоны бачнасці ў JavaScript, уключаючы глабальную зону бачнасці, маюць +адмысловае імя this, аб'яўленае ўнутры іх, якое спасылаецца +на актуальны аб'ект.

    +

    Зоны бачнасці функцый таксама маюць імя arguments, +аб'яўленае ў іх, якое спасылаецца на аргументы, што былі перададзеныя ў функцыю.

    +

    Напрыклад, калі паспрабаваць атрымаць доступ да пераменнай foo унутры зоны +бачнасці функцыі, JavaScript будзе шукаць імя ў наступным парадку:

    +
      +
    1. У выпадку калі прысутнічае канструкцыя var foo у актуальнай зоне бачнасці, +ёна і выкарыстоўваецца.
    2. +
    3. Калі параметр функцыі мае імя foo, ён будзе выкарыстаны.
    4. +
    5. Калі сама функцыя называецца foo, яна будзе выкарыстана.
    6. +
    7. Пераходзіць у знешнюю зону бачнасці, і пачынае з пункта #1.
    8. +
    + +

    Прасторы імёнаў

    +

    Вялікая праблема, звязаная з выкарыстоўваннем глабальнай прасторы імёнаў, гэта +высокая верагоднасць перасячэння імёнаў пераменных. У JavaScript, гэта праблема +можа быць лёгка пазбегнута праз выкарыстанне ананімных абгортак.

    +
    (function() {
    +    // аўтаномная "прастора імён"
    +
    +    window.foo = function() {
    +        // адкрытае замыканне
    +    };
    +
    +})(); // імгненнае выкананне функцыі
    +

    Ананімныя функцыі з'яўляюцца выразамі; таму каб быць выкліканымі, +яны спачатку маюць быць ацэненымі.

    +
    ( // ацэньваем функцыю ўнутры дужак
    +function() {}
    +) // вяртаем аб'ект функцыі
    +() // выклік выніку ацэнкі
    +

    Ёсць і іншыя спосабы ацаніць і імгненна выклікаць выраз функцыі, хаця яны і +адрозніваюцца па сінтаксісу, паводзяць сябе аднолькава.

    +
    // Яшчэ некалькі спосабаў наўпрост выклікаць функцыю
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +// і так далей...
    +

    Заключэнне

    +

    Рэкамендуецца заўсёды выкарыстоўваць ананімную абгортку каб інкапсуліраваць код +у яго асабістай прасторы імёнаў. Гэта не толькі абараняе код ад перасячэння імёнаў, +але і дапамагае падзяляць праграму на модулі.

    +

    Таксама выкарыстанне глабальных пераменных лічыцца дрэннай практыкай. Любое +іх выкарыстоўванне - прыкмета дрэнна напісанага кода, схільнага да памылак, і цяжага +ў падтрымцы.

    +

    Масівы

    Ітэрацыі па масівам і ўласцівасці

    Хоць масівы ў JavaScript — аб'екты, няма добрых падставаў для таго, каб +выкарыстоўваць цыкл for in для ітэрацыі па масівах. +Фактычна, ёсць шэраг добрых падстаў супраць гэтага.

    + +

    З той прычыны, што цыкл for in пералічвае ўсе ўласцівасці, якія ёсць у ланцужку прататыпаў, +і таму, што адзіны спосаб выключыць гэтыя значэнні — hasOwnProperty, +ітэрацыя атрымліваецца ў 20 разоў марудней за звычайны цыкл for.

    +

    Ітэрацыя

    +

    Для таго, каб атрымаць найлепшую прадукцыйнасць у ітэрацыі па масіву, лепш +выкарыстаць класічны цыкл for.

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

    У вышэйпрыведзеным прыкладзе ёсць яшчэ адзін прыём, з дапамогай якога можна кэшаваць +памер масіва: l = list.length.

    +

    Негледзячы на тое, што ўласцівасць length вызначана ў самім масіве, пошук +гэтай уласцівасці накладвае выдаткі на пошук пры кожнай ітэрацыі цыкла. І хоць +новыя рухавікі JavaScript могуць прымяніць аптымізацыю у гэтым выпадку, няма +магчымасці дакладна ведаць, ці будзе код выкананы на гэтых новых рухавіках.

    +

    Фактычна, адсутнасць кэшавання можа зрабіць выкананне цыкла ў два разы больш +марудным, чым з кэшаваным 'length'.

    +

    Уласцівасць length

    +

    Хоць гэтэр уласцівасці length проста вяртае колькасць элементаў, што +знаходзяцца у масіве, сэтэр можа быць выкарыстаны для абразання масіва.

    +
    var arr = [1, 2, 3, 4, 5, 6];
    +arr.length = 3;
    +arr; // [1, 2, 3]
    +
    +arr.length = 6;
    +arr.push(4);
    +arr; // [1, 2, 3, undefined, undefined, undefined, 4]
    +

    Прысвойванне ўласцівасці 'length' меншага значэння абразае масіў. Прысваенне большага +значэнне створыць разрэджаны масіў.

    +

    У заключэнне

    +

    Для лепшай прадукцыйнасці рэкамендуецца заўсёды выкарыстоўваць звычайны цыкл for, +і кэшаваць уласціваць length. Выкарыстоўванне for in для ітэрацыі па масіву — +прыкмета дрэнна напісанага коду, схільнага да памылак і дрэннай прадукцыйнасці.

    +

    Канструктар Array

    Праз тое, што канструктар Array неадназначна апрацоўвае свае параметры, +крайне рэкамендуецца выкарыстоўваць літэрал - [] - для стварэння масіваў.

    +
    [1, 2, 3]; // Вынік: [1, 2, 3]
    +new Array(1, 2, 3); // Вынік: [1, 2, 3]
    +
    +[3]; // Вынік: [3]
    +new Array(3); // Вынік: []
    +new Array('3') // Вынік: ['3']
    +

    У выпадку, калі канструктару Array перадаецца толькі адзін параметр, і калі гэты +аргумент тыпу Number, канструктар верне разрэджаны масіў, які мае уласціваць +length са значэннем аргумента. Варта адзначыць, што такім чынам будзе зменена +толькі значэнне ўласцівасці length масіва; індэксы масіва не будуць +праініцыялізаваныя.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, індэкс не праініцыялізаваны
    +

    Магчымасць загадзя вызначыць даўжыню масіва карысна толькі ў рэдкіх выпадках, +напрыклад, паўтор радка без выкарыстання цыкла.

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

    У заключэнне

    +

    Літэралы маюць перавагі над канструктарам Array. Яны карацейшыя, маюць больш чысты +сінтаксіс і робяць код больш чытэльным.

    +

    Тыпы

    Роўнасць і параўнанне

    У JavaScript роўнасць значэнняў аб'ектаў можна вызначыць двумя спосабамі.

    +

    Аператар роўнасці

    +

    Аператар роўнасці складаецца з двух сімвалаў 'роўна': ==

    +

    JavaScript мае слабую тыпізацыю. Гэта значыць што аператар роўнасці +прыводзіць тыпы аб'ектаў, каб параўнаць іх.

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

    Вышэй прыведзеная табліца паказвае вынікі прывядзення тыпаў, і гэта галоўная прычына +па якой выкаростоўванне == лічыцца дрэннай практыкай. Яно прыводзіць да памылак +якія цяжка адсачыць праз складаны механізм прывядзення тыпаў.

    +

    Акрамя гэтага, прывядзенне тыпаў таксама ўплывае на вытворчасць; +напрыклад, радок мае быць ператвораны ў нумар, перад тым як быць параўнаным з +іншым нумарам.

    +

    Аператар строгай роўнасці

    +

    Аператар строгай роўнасці складаецца з трох сімвалаў 'роўна': ===.

    +

    Ён дзейнічае як звычайны аператар роўнасці, за выключэннем таго, што строгая +роўнасць не прыводзіць аперанды да агульнага тыпу.

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

    Вышэй прыведзеныя вынікі значна больш зразумелыя і даюць магчымасць хутчэй выявіць +памылкі ў кодзе. Гэта паляпшае код, а таксама дае прырост вытворчасці, у выпадку +калі аперанды розных тыпаў.

    +

    Параўнанне аб'ектаў

    +

    Хоць абодва аператар == і === называюцца аператарамі роўнасці, яны паводзяць +сабе па рознаму калі хоць адзін аперанд тыпа Object.

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

    Тут абодва аператанда параўноўваюцца на ідэнтычнасць, а не на роўнасць; +то бок будзе праверана, ці з'яўляюцца яны адным экзэмплярам аб'екта. Гэтак жа, +як is у Python, або параўнанне ўказальнікаў у C.

    +

    У заключэнне

    +

    Настойліва рэкамендуецца выкарыстоўваць толькі аператар строгай роўнасці. +У выпадку, калі тыпы маюць быць прыведзеныя, гэта варта рабіць яўна, +а не пакідаць іх на сумленні складаных правілаў прывядзення мовы праграмавання.

    +

    Аператар typeof

    Аператар typeof (разам з instanceof) магчыма найбольшая +хіба мовы JavaScript, таму што ён амаль што цалкам зламаны.

    +

    Хаця instanceof усё яшчэ мае абмежаванае ўжыванне, typeof можа быць выкарыстаны +толькі з адной мэтай, і гэта дарэчы не праверка тыпа.

    + +

    Табліца тыпаў JavaScript

    +
    Значэнне            Клас       Тып
    +-------------------------------------
    +"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
    +

    У вышэй прыведзенай табыліцы, Тып паказвае значэнне вернутае аператарам typeof. +Як можна пабачыць, гэта значэнне абсалютна не кансістэнтнае.

    +

    Клас паказвае значэнне ўнутраннай уласцівасці [[Class]] аб'екта.

    + +

    Клас аб'екта

    +

    Адзіны спосаб атрымаць значэнне [[Class]] аб'екта - выклікаць метад Object.prototype.toString. +Ён верне радок у наступным фармаце: '[object ' + valueOfClass + ']', напрыклад +[object String] або [object Array]:

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

    У вышэйпрыведзеным прыкладзе, Object.prototype.toString выклікаецца са значэннем +this пазначаным як аб'ект чыё значэнне [[Class]] мае быць +атрыманым.

    + +

    Праверка вызначанасці пераменных

    +
    typeof foo !== 'undefined'
    +

    Вышэйпрыведзены код праверыць ці было вызначана foo; просты зварот да пераменнай +прывядзе да ReferenceError. Гэта адзінае для чаго карысны typeof.

    +

    У заключэнне

    +

    Каб праверыць тып аб'екта, настойліва рэкамендуецца выкарыстоўваць +Object.prototype.toString - гэта адзіны надзейны спосаб. +Як паказана ў вышэйпрыведзенай табліцы, некаторыя значэнні вернутыя аператарам +typeof не вызначаныя ў спецыфікацыі; такім чынам, яны могуць быць рознымі ў +розных рэалізацыях.

    +

    Акрамя як для праверкі вызначанасці пераменнай, typeof мае быць пазбегнуты.

    +

    Аператар instanceof

    Аператар instanceof параўноўвае канструктары двух аперандаў. Гэта карысна толькі +для параўнання аб'ектаў не ўбудаваных тыпаў. Выкарыстоўванне на ўбудаваных тыпах не +мае сэнсу, як і аператар typeof.

    +

    Параўнанне адвольных аб'ектаў

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Калі толькі прысвоім Bar.prototype аб'ект функцыі Foo,
    +// але не самаго экзэмпляра Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    Выкарыстоўванне instanceof з убудаванымі тыпамі

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

    Варта адзначыць, што instanceof не працуе на аб'ектах, якія паходзяць з розных +кантэкстаў JavaScript (напрыклад, розных дакументаў у web-браузеры), бо іх канструктары +насамрэч не будуць канструктарамі тых самых аб'ектаў.

    +

    У заключэнне

    +

    Аператар instanceof мае быць выкарыстаны толькі для працы з аб'ектамі не +ўбудаваных тыпаў якія паходзяць з аднаго кантэкста JavaScript. Як і ў выпадку з +аператарам typeof, трэба пазбягаць любога іншага яго выкарыстання.

    +

    Прывядзенне тыпаў

    JavaScript - слаба тыпізаваная мова, таму прывядзенне тыпаў адбываецца +паўсюль дзе магчыма.

    +
    // Гэтыя равенствы - праўдзівыя
    +new Number(10) == 10; // аб'ект тыпа Number пераўтвараецца у
    +                      // лікавы прымітыў, праз няяўны выклік
    +                      // метада Number.prototype.valueOf
    +
    +10 == '10';           // Strings пераўтвараецца ў Number
    +10 == '+10 ';         // Троху вар'яцтва з радкамі
    +10 == '010';          // і яшчэ
    +isNaN(null) == false; // null пераўтвараецца ў 0
    +                      // які вядома ж не NaN
    +
    +// Гэтыя равенствы - ілжывыя
    +10 == 010;
    +10 == '-10';
    + +

    Каб пазбегнуць вышэйпрыведзеных праблемаў, настойліва ракамендуецца выкарыстоўваць +аператар строгай роўнасці. Зрэшты, хоць гэта і пазбаўляе ад +многіх распаўсюджаных праблемаў, існуе яшчэ шмат праблемаў, які ўзнікаюць праз +слабую тыпізацыю JavaScript.

    +

    Канструктары ўбудаваных тыпаў

    +

    Канструктары ўбудаваных тыпаў, напрыклад, Number і String паводзяць сябе +па рознаму, у залежнасці ад таго, выклікаюцца яны з ключавым словам new або без яго.

    +
    new Number(10) === 10;     // False, Object і Number
    +Number(10) === 10;         // True, Number і Number
    +new Number(10) + 0 === 10; // True, праз неяўнае прывядзенне
    +

    Выкарыстанне ўбудаванага тыпу, такога як Number у якасці канструкта створыць новы +экзэмпляр аб'екта Number, але пры адсутнасці ключавога слова new функцыя +Number будзе паводзіць сябе як канвертар.

    +

    У дадатак, выкарытоўванне літэралаў, або значэнняў якія не з'яўляюцца аб'ектамі +прывядзе да дадатковых прывядзенняў тыпаў.

    +

    Лепшы варыянт - гэта яўнае прывядзенне да аднаго з трох магчымых тыпаў.

    +

    Прывядзенне да радка

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

    Праз даданне да значэння пустога радка, яно лёгка прыводзіцца да радка.

    +

    Прывядзенне да лікавага тыпу

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

    Выкарыстоўваючы унарны аператар плюс, магчыма пераўтварыць значэнне ў нумар.

    +

    Прывядзенне да булевага тыпу

    +

    Выкарыстоўваючы аператар адмаўленне (!) двойчы, значэнне можна прыведзена +да лагічнага (булевага) тыпу.

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

    Ядро

    Чаму не варта выкарыстоўваць eval

    Функцыя eval выконвае радок JavaScript коду ў лакальнай зоне бачнасці.

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

    Тым не менш, eval выконваецца ў лакальнай прасторы імён толькі ў тым выпадку, калі +яна была выклікана наўпрост і імя выкліканай функцыі — eval.

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

    Лепш пазбягаць выкарыстоўвання eval. 99,9% яе «выкарыстанняў» можа быць дасягнута +без яе.

    +

    Схаваны eval

    +

    Абедзве функцыі тайм-аўты setTimeout і setInterval могуць +прымаць радок у якасці першага аргумента. Гэты радок будзе заўсёды выконвацца +ў глабальнай прасторы імёнаў, бо eval не выклікаецца наўпрост у дадзеным выпадку.

    +

    Праблемы з бяспекаю

    +

    Таксама eval мае праблемы з бяспекаю, бо ён выконвае любы перададзены код. +Таму яе ніколі не варта выкарыстоўваць з радкамі, што паходзяць з ненадзейных +крыніцаў.

    +

    Заключэнне

    +

    Лепш ніколі не выкарыстоўваць eval. Любы код, што выкарыстоўвае яе, спрэчны ў +плане якасці, карэктнасці, прадукцыйнасці і бяспекі. Калі для таго, каб нешта +працавала, патрэбны eval, не трэба прымаць гэта рашэнне ў першую чаргу. +Лепшым рашэннем будзе тое, што не будзе выкарыстоўваць eval.

    +

    undefined і null

    JavaScript мае два розныя значэнні для 'нічога' - гэта null і undefined, пры +гэтым апошняе больш карыснае.

    +

    Значэнне undefined

    +

    undefined — гэта тып з роўна адным значэннем: undefined.

    +

    Мова таксама аб'яўляе глабальную пераменную, што мае значэнне undefined; Гэта +пераменная таксама называецца undefined. Тым не менш, гэта пераменная, а не +канстанта, ці ключавое слова. Гэта азначае, што яе значэнне можа быць +з лёгкасцю перазапісаным.

    + +

    Ніжэй пералічаныя некалькі выпадкаў, калі вяртаецца undefined:

    +
      +
    • Доступ да (немадыфікаванай) глабальнай пераменнай undefined.
    • +
    • Доступ да аб'яўленай, але яшчэ не ініцыалізаванай пераменнай.
    • +
    • Няяўна вернутае значэнне функцыі праз адсутнасць аператара return.
    • +
    • З аператара return, які нічога яўна не вяртае.
    • +
    • У выніку пошуку неіснуючай уласцівасці аб'екта.
    • +
    • Параметры функцыі, якім яўна не было прысвоена значэнне.
    • +
    • Усё, чаму было прысвоена значэнне undefined.
    • +
    • Любы выраз у форме void(expression).
    • +
    +

    Апрацоўка зменаў значэння undefined

    +

    З той прычыны, што глабальная пераменная undefined утрымлівае толькі копію +актуальнага значэння undefined, прысвойванне ёй новага значэння не мяняе +значэнне тыпа undefined.

    +

    Таксама для таго, каб параўнаць што-небудзь з значэннем undefined, спачатку +трэба атрымаць значэнне undefined.

    +

    Звыклая тэхніка абароны ад магчымага перазапісвання пераменнай undefined — +дадатковы параметр у ананімнай абгортцы, што выкарыстоўвае +адсутны аргумент.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // цяпер undefined у лакальнай зоне бачнасці
    +    // зноў спасылаецца на значэнне `undefined`
    +
    +})('Hello World', 42);
    +

    Гэтага ж выніку можна дасягнуць праз аб'яўленне ўнутры абгорткі.

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

    Адзіная розніца ў тым, што гэта апошняя версія будзе большай на 4 байты пры +мініфікацыі, а ў першым унутры ананімнай абгорткі не будзе аператара var.

    +

    Выкарыстоўванне null

    +

    Хаця undefined у кантэксце мовы JavaScript у асноўным выкарыстоўваецца ў сэнсе +традыйнага null, сам null (і літэрал і тып) з'яўляецца яшчэ адным тыпам дадзеных.

    +

    Выкарыстоўваецца ў некаторых унутранных механізмах JavaScript (напрыклад аб'яўленне +канца ланцужка прататыпаў пазначаючы Foo.prototype = null), але амаль што ва ўсіх +выпадках ён можа быць заменены на undefined.

    +

    Аўтаматычная ўстаўка кропкі з коскай

    Хаця JavaScript мае C-падобны сінтакс, ён не прымушае выкарыстоўваць кропку +з коскай у кодзе, таму ёсць магчымасць прапускаць іх.

    +

    Але JavaScript — не мова без кропак з коскай. Насамрэч яны патрэбны ёй, каб разумець +зыходны код. Таму парсер JavaScript аўтаматычна ўстаўляе іх паўсюль, дзе +сустракае памылку адсутнасці кропкі з коскай.

    +
    var foo = function() {
    +} // памылка разбора, парсер чакаў кропку з коскай
    +test()
    +

    Адбываецца ўстаўка, парсер спрабуе зноў.

    +
    var foo = function() {
    +}; // памылкі няма, парсер працягвае
    +test()
    +

    Аўтаматычная ўстаўка кропкі з коскай лічыцца адной з найвялікшых архітэктурных +памылак у мове, бо можа змяніць паводзіны кода.

    +

    Як яно працуе

    +

    Ніжэйпрыведзены код не мае кропак з коскай, таму парсер вырашае дзе іх уставіць.

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

    Ніжэй — вынік гульні парсера ў адгадванне.

    +
    (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; // <- устаўлена, разбіў аператар return на два блока
    +        { // парсер лічыць гэты блок асобным
    +            foo: function() {}
    +        }; // <- устаўлена
    +    }
    +    window.test = test; // <- устаўлена
    +
    +// Радкі зноў аб'ядналіся
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- устаўлена
    +
    +})(window); //<- устаўлена
    + +

    Парсер кардынальна памяняў паводзіны кода. У пэўных выпадках ён прымае памылковыя +рашэнні.

    +

    Вядучыя дужкі

    +

    У выпадку вядучай дужкі парсер не уставіць кропку з коскай.

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

    Гэты код ператворыцца ў радок.

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

    Вельмі верагодна, што log не вяртае функцыю; Таму вышэйпрыведзены +код справакуе TypeError з заявай, што undefined не з'яўляецца функцыяй.

    +

    Заключэнне

    +

    Крайне рэкамендуецца ніколі не прапускаць кропку з коскай. Таксама заўсёды +рэкамендуецца ставіць дужкі на той жа лініі, што і адпаведныя канструкцыі, і ніколі +не прапускаць іх у аднарадковых канструкцыях if / else. Гэтыя меры не толькі +павысяць кансістэнтнасць кода, але таксама прадухіляць ад таго, што парсер +JavaScript зменіць паводзіны кода.

    +

    Аператар delete

    У JavaScript немагчыма выдаліць глабальныя пераменныя, функцыі і некаторыя іншыя +рэчы, што маюць атрыбут DontDelete.

    +

    Глабальны код і код функцый.

    +

    Калі пераменная або функцыя аб'яўленыя ў глабальнай зоне бачнасці, або зоне бачнасці +функцыі то яна будзе ўласцівасцю або аб'екта актывацыі, або +глабальнай зоны бачнасці. Такія ўласцівасці маюць набор атрыбутаў, адзін з іх — +DontDelete. Пераменныя і функцыі, аб'яўленыя ў глабальнай зоне бачнасці і зоне +бачнасці функцыі, заўсёды ствараюцца з уласцівасцю DontDelete, а таму не могуць +быць выдаленыя.

    +
    // глабальная пераменная:
    +var a = 1; // мае ўласцівасць DontDelete
    +delete a; // false
    +a; // 1
    +
    +// звычайная функцыя:
    +function f() {} // мае ўласціваць DontDelete
    +delete f; // false
    +typeof f; // "function"
    +
    +// перапрызначэнне не дапамагае:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    Яўна прызначаныя ўласцівасці

    +

    Яўна прызначаныя ўласцівасці могуць быць лёгка выдаленыя.

    +
    // яўна прызначаныя ўласцівасці:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    У вышэйпрыведзеным прыкладзе, obj.x і obj.y могуць быць выдаленыя, бо не маюць +атрыбута DontDelete. Таму і прыкад ніжэй працуе:

    +
    // Гэта працуе правільна, акрамя IE:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true — глабальная пераменная
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    Гэта прыём, каб выдаліць a. this спасылаецца на глабальны +аб'ект, і мы яўна аб'яўляем пераменную a як яго ўласцівасць, што дазваляе нам +выдаліць яе.

    +

    IE (прынамсі 6-8) мае хібы, таму вышэйпрыведзены код там працаваць не будзе.

    +

    Аргументы функцый і ўбудаваныя ўласцівасці

    +

    Звычайныя аргументы функцыі, аб'ект arguments, а таксама +убудаваныя ўласцівасці таксама маюць атрыбут DontDelete.

    +
    // аргументы функцыі і ўласцівасці:
    +(function (x) {
    +
    +  delete arguments; // false
    +  typeof arguments; // "object"
    +
    +  delete x; // false
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // false
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    У заключэнне

    +

    Аператар delete часта паводзіць сябе нечакана, таму адзінае надзейнае выкарыстанне +delete — выдаленне яўна прызначаных уласцівасцяў.

    +

    Рэшта

    setTimeout і setInterval

    Дзякуючы асінхроннасці JavaScript магчыма запланаваць выкананне функцыі з дапамогай +метадаў setTimeout і setInterval.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // вяртае Number > 0
    +

    Калі функцыя setTimeout выклікана, яна вяртае ID таймаўта і плануе выкананне +foo прыблізна праз тысячу мілісекунд. +foo будзе выканана аднойчы.

    +

    Улічваючы вырашэнні таймера рухавіка Javascript, які выконвае код, +аднапаточнасць JavaScript і тое, што іншы код, які выконваецца +можа блакаваць паток, нельга быць упэўненым, што вы атрымаеце затрымку, +пазначанаю ў выкліку setTimeout.

    +

    Функцыя, якая была перададзена як першы параметр, будзе выклікана +глабальным аб'ектам, гэта азначае, што this унутры выкліканай функцыі +спасылаецца на глабальны аб'ект.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this спасылаецца на глабальны аб'ект
    +        console.log(this.value); // выведзе undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Паслядоўныя выклікі з дапамогай setInterval

    +

    У той час калі setTimeout выконвае функцыю толькі адзін раз, setInterval - як бачна з назвы - +выконвае функцыю кожныя X milliseconds, але яе выкарыстанне не пажадана.

    +

    Код, які выконваецца, блакуе выклік з таймаўтам, у той час setInterval будзе +планаваць выклікі зададзенай функцыі. Гэта можа, асабліва з маленькімі інтэрваламі, +прывесці да стварэння чаргі выклікаў функцый.

    +
    function foo(){
    +    // нешта, што блакуе на 1 секунду
    +}
    +setInterval(foo, 100);
    +

    У прыведзеным кодзе foo будзе выклікана аднойчы і заблакуе выкананне на адну секунду.

    +

    У той час калі foo блакуе код, setInterval будзе планаваць наступныя яе выклікі +А калі выкананне foo скончана, ужо дзесяць наступных выклікаў будуць чакаць выканання.

    +

    Праца з магчыма блакуючым кодам

    +

    Найбольш простае і кіруемае рашэнне гэта выкарыстоўваць setTimeout унутры самой функцыі.

    +
    function foo(){
    +    // нешта, што блакуе на 1 секунду
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Гэты падыход не толькі інкапсулюе выклік setTimeout, але таксама прадухіляе +стварэнне чаргі выклікаў і дае дадатковы кантроль. Цяпер foo можа сама вырашыць +хоча яна выконвацца яшчэ раз ці не.

    +

    Ручная чыстка таймаўтаў

    +

    Чыстка таймаўтаў і інтэрвалаў здзяйсняецца перадачай адпаведнага ID у +clearTimeout або clearInterval, гледзячы якая set функцыя была выкарыстана +да гэтага.

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

    Чыстка ўсіх таймаўтаў

    +

    Так як няма ўбудаванага метада для выдалення ўсіх таймаўтаў і/або інтэрвалаў, +для гэтага неабходна выкарыстоўваць брутфорс.

    +
    // выдаліць "усе" таймаўты
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Але могуць быць таймаўты, якія не закрануты гэтым адвольным нумарам. +Іншы шлях ажыццяўлення гэтага - прыняць, што ID таймаўта павялічваецца на +адзін пасля кожнага выкліку setTimeout.

    +
    // выдаліць "усе" таймаўты
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    Не гледзячы на тое, што зараз гэта працуе на ўсіх асноўных браўзерах, тое, што ID +павінны быць арганізаваны такім шляхам не пазначана ў спецыфікацыі і можа змяніцца. Таму +замест гэтага рэкамендуецца сачыць за ўсімі ID таймаўтаў, каб яны маглі быць выдалены паасобку.

    +

    Схаванае выкарыстанне eval

    +

    setTimeout і setInterval таксама могуць прымаць радок у якасці першага параметра. +Гэту магчымасць ніколі не трэба выкарыстоўваць, бо ўнутрана вызываецца eval.

    + +
    function foo() {
    +    // будзе выклікана
    +}
    +
    +function bar() {
    +    function foo() {
    +        // ніколі не будзе выклікана
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Паколькі eval не выклікана напрамую, радок, які перададзены +setTimeout будзе выкананы ў глабальным скоўпе; такім чынам, не будзе выкарыстана +лакальная пераменная foo са скоўпа bar.

    +

    Адсюль вынікае рэкамендацыя не выкарыстоўваць радок для перадачы аргументаў у +функцыю, якая будзе вызывацца адной з таймаўт функцый.

    +
    function foo(a, b, c) {}
    +
    +// НІКОЛІ так не рабіце
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// Замест выкарыстоўвайце ананімныя функцыі
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    У заключэнне

    +

    Радок ніколі не павінен быць выкарыстаны як параметр setTimeout ці +setInterval. Відавочны знак сапраўды благога кода гэта калі функцыя, якая будзе выклікана, +патрабуе аргументы. Трэба перадаваць ананімную функцыю, якая будзе адказваць за +выклік патрэбнай функцыі.

    +

    Больш таго, трэба пазбягаць выкарыстання setInterval, таму што яе планавальнік не блакуецца +выкананнем JavaScript.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/by/array/constructor.md b/external/JavaScript-Garden/doc/by/array/constructor.md new file mode 100644 index 0000000..6eaa5f2 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/array/constructor.md @@ -0,0 +1,31 @@ +## Канструктар `Array` + +Праз тое, што канструктар `Array` неадназначна апрацоўвае свае параметры, +крайне рэкамендуецца выкарыстоўваць літэрал - `[]` - для стварэння масіваў. + + [1, 2, 3]; // Вынік: [1, 2, 3] + new Array(1, 2, 3); // Вынік: [1, 2, 3] + + [3]; // Вынік: [3] + new Array(3); // Вынік: [] + new Array('3') // Вынік: ['3'] + +У выпадку, калі канструктару `Array` перадаецца толькі адзін параметр, і калі гэты +аргумент тыпу `Number`, канструктар верне *разрэджаны* масіў, які мае уласціваць +`length` са значэннем аргумента. Варта адзначыць, што такім чынам будзе зменена +**толькі** значэнне ўласцівасці `length` масіва; індэксы масіва не будуць +праініцыялізаваныя. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, індэкс не праініцыялізаваны + +Магчымасць загадзя вызначыць даўжыню масіва карысна толькі ў рэдкіх выпадках, +напрыклад, паўтор радка без выкарыстання цыкла. + + new Array(count + 1).join(stringToRepeat); + +### У заключэнне + +Літэралы маюць перавагі над канструктарам Array. Яны карацейшыя, маюць больш чысты +сінтаксіс і робяць код больш чытэльным. diff --git a/external/JavaScript-Garden/doc/by/array/general.md b/external/JavaScript-Garden/doc/by/array/general.md new file mode 100644 index 0000000..3e358d5 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/array/general.md @@ -0,0 +1,56 @@ +## Ітэрацыі па масівам і ўласцівасці + +Хоць масівы ў JavaScript — аб'екты, няма добрых падставаў для таго, каб +выкарыстоўваць [цыкл `for in`](#object.forinloop) для ітэрацыі па масівах. +Фактычна, ёсць шэраг добрых падстаў **супраць** гэтага. + +> **Заўвага:** масівы JavaScript **не** *асацыятыўныя масівы*. JavaScript мае +> [аб'екты](#object.general) для структуры ключ-значэнне. Але калі асацыятыўныя +> масівы **захоўваюць** парадак, аб'екты — **не**. + +З той прычыны, што цыкл `for in` пералічвае ўсе ўласцівасці, якія ёсць у ланцужку прататыпаў, +і таму, што адзіны спосаб выключыць гэтыя значэнні — [`hasOwnProperty`](#object.hasownproperty), +ітэрацыя атрымліваецца ў **20 разоў** марудней за звычайны цыкл `for`. + +### Ітэрацыя + +Для таго, каб атрымаць найлепшую прадукцыйнасць у ітэрацыі па масіву, лепш +выкарыстаць класічны цыкл `for`. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +У вышэйпрыведзеным прыкладзе ёсць яшчэ адзін прыём, з дапамогай якога можна кэшаваць +памер масіва: `l = list.length`. + +Негледзячы на тое, што ўласцівасць `length` вызначана ў самім масіве, пошук +гэтай уласцівасці накладвае выдаткі на пошук пры кожнай ітэрацыі цыкла. І хоць +новыя рухавікі JavaScript **могуць** прымяніць аптымізацыю у гэтым выпадку, няма +магчымасці дакладна ведаць, ці будзе код выкананы на гэтых новых рухавіках. + +Фактычна, адсутнасць кэшавання можа зрабіць выкананне цыкла **ў два разы больш +марудным**, чым з кэшаваным 'length'. + +### Уласцівасць `length` + +Хоць *гэтэр* уласцівасці `length` проста вяртае колькасць элементаў, што +знаходзяцца у масіве, *сэтэр* можа быць выкарыстаны для **абразання** масіва. + + var arr = [1, 2, 3, 4, 5, 6]; + arr.length = 3; + arr; // [1, 2, 3] + + arr.length = 6; + arr.push(4); + arr; // [1, 2, 3, undefined, undefined, undefined, 4] + +Прысвойванне ўласцівасці 'length' меншага значэння абразае масіў. Прысваенне большага +значэнне створыць разрэджаны масіў. + +### У заключэнне + +Для лепшай прадукцыйнасці рэкамендуецца заўсёды выкарыстоўваць звычайны цыкл `for`, +і кэшаваць уласціваць `length`. Выкарыстоўванне `for in` для ітэрацыі па масіву — +прыкмета дрэнна напісанага коду, схільнага да памылак і дрэннай прадукцыйнасці. diff --git a/external/JavaScript-Garden/doc/by/core/delete.md b/external/JavaScript-Garden/doc/by/core/delete.md new file mode 100644 index 0000000..07e2c1c --- /dev/null +++ b/external/JavaScript-Garden/doc/by/core/delete.md @@ -0,0 +1,81 @@ +## Аператар `delete` + +У JavaScript *немагчыма* выдаліць глабальныя пераменныя, функцыі і некаторыя іншыя +рэчы, што маюць атрыбут `DontDelete`. + +### Глабальны код і код функцый. + +Калі пераменная або функцыя аб'яўленыя ў глабальнай зоне бачнасці, або [зоне бачнасці +функцыі](#function.scopes) то яна будзе ўласцівасцю або аб'екта актывацыі, або +глабальнай зоны бачнасці. Такія ўласцівасці маюць набор атрыбутаў, адзін з іх — +`DontDelete`. Пераменныя і функцыі, аб'яўленыя ў глабальнай зоне бачнасці і зоне +бачнасці функцыі, заўсёды ствараюцца з уласцівасцю `DontDelete`, а таму не могуць +быць выдаленыя. + + // глабальная пераменная: + var a = 1; // мае ўласцівасць DontDelete + delete a; // false + a; // 1 + + // звычайная функцыя: + function f() {} // мае ўласціваць DontDelete + delete f; // false + typeof f; // "function" + + // перапрызначэнне не дапамагае: + f = 1; + delete f; // false + f; // 1 + +### Яўна прызначаныя ўласцівасці + +Яўна прызначаныя ўласцівасці могуць быць лёгка выдаленыя. + + // яўна прызначаныя ўласцівасці: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +У вышэйпрыведзеным прыкладзе, `obj.x` і `obj.y` могуць быць выдаленыя, бо не маюць +атрыбута `DontDelete`. Таму і прыкад ніжэй працуе: + + // Гэта працуе правільна, акрамя IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true — глабальная пераменная + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Гэта прыём, каб выдаліць `a`. [`this`](#function.this) спасылаецца на глабальны +аб'ект, і мы яўна аб'яўляем пераменную `a` як яго ўласцівасць, што дазваляе нам +выдаліць яе. + +IE (прынамсі 6-8) мае хібы, таму вышэйпрыведзены код там працаваць не будзе. + +### Аргументы функцый і ўбудаваныя ўласцівасці + +Звычайныя аргументы функцыі, [аб'ект `arguments`](#function.arguments), а таксама +убудаваныя ўласцівасці таксама маюць атрыбут `DontDelete`. + + // аргументы функцыі і ўласцівасці: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### У заключэнне + +Аператар `delete` часта паводзіць сябе нечакана, таму адзінае надзейнае выкарыстанне +`delete` — выдаленне яўна прызначаных уласцівасцяў. diff --git a/external/JavaScript-Garden/doc/by/core/eval.md b/external/JavaScript-Garden/doc/by/core/eval.md new file mode 100644 index 0000000..7e07dbc --- /dev/null +++ b/external/JavaScript-Garden/doc/by/core/eval.md @@ -0,0 +1,47 @@ +## Чаму не варта выкарыстоўваць `eval` + +Функцыя `eval` выконвае радок JavaScript коду ў лакальнай зоне бачнасці. + + var number = 1; + function test() { + var number = 2; + eval('number = 3'); + return number; + } + test(); // 3 + number; // 1 + +Тым не менш, `eval` выконваецца ў лакальнай прасторы імён толькі ў тым выпадку, калі +яна была выклікана наўпрост *і* імя выкліканай функцыі — `eval`. + + var number = 1; + function test() { + var number = 2; + var copyOfEval = eval; + copyOfEval('number = 3'); + return number; + } + test(); // 2 + number; // 3 + +Лепш пазбягаць выкарыстоўвання `eval`. 99,9% яе «выкарыстанняў» можа быць дасягнута +**без** яе. + +### Схаваны `eval` + +Абедзве [функцыі тайм-аўты](#other.timeouts) `setTimeout` і `setInterval` могуць +прымаць радок у якасці першага аргумента. Гэты радок будзе **заўсёды** выконвацца +ў глабальнай прасторы імёнаў, бо `eval` не выклікаецца наўпрост у дадзеным выпадку. + +### Праблемы з бяспекаю + +Таксама `eval` мае праблемы з бяспекаю, бо ён выконвае **любы** перададзены код. +Таму яе **ніколі** не варта выкарыстоўваць з радкамі, што паходзяць з ненадзейных +крыніцаў. + +### Заключэнне + +Лепш ніколі не выкарыстоўваць `eval`. Любы код, што выкарыстоўвае яе, спрэчны ў +плане якасці, карэктнасці, прадукцыйнасці і бяспекі. Калі для таго, каб нешта +працавала, патрэбны `eval`, **не** трэба прымаць гэта рашэнне ў першую чаргу. +*Лепшым рашэннем* будзе тое, што не будзе выкарыстоўваць `eval`. diff --git a/external/JavaScript-Garden/doc/by/core/semicolon.md b/external/JavaScript-Garden/doc/by/core/semicolon.md new file mode 100644 index 0000000..db78ed7 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/core/semicolon.md @@ -0,0 +1,109 @@ +## Аўтаматычная ўстаўка кропкі з коскай + +Хаця JavaScript мае C-падобны сінтакс, ён **не** прымушае выкарыстоўваць кропку +з коскай у кодзе, таму ёсць магчымасць прапускаць іх. + +Але JavaScript — не мова без кропак з коскай. Насамрэч яны патрэбны ёй, каб разумець +зыходны код. Таму парсер JavaScript **аўтаматычна** ўстаўляе іх паўсюль, дзе +сустракае памылку адсутнасці кропкі з коскай. + + var foo = function() { + } // памылка разбора, парсер чакаў кропку з коскай + test() + +Адбываецца ўстаўка, парсер спрабуе зноў. + + var foo = function() { + }; // памылкі няма, парсер працягвае + test() + +Аўтаматычная ўстаўка кропкі з коскай лічыцца адной з **найвялікшых** архітэктурных +памылак у мове, бо *можа* змяніць паводзіны кода. + +### Як яно працуе + +Ніжэйпрыведзены код не мае кропак з коскай, таму парсер вырашае дзе іх уставіць. + + (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) + +Ніжэй — вынік гульні парсера ў адгадванне. + + (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; // <- устаўлена, разбіў аператар return на два блока + { // парсер лічыць гэты блок асобным + foo: function() {} + }; // <- устаўлена + } + window.test = test; // <- устаўлена + + // Радкі зноў аб'ядналіся + })(window)(function(window) { + window.someLibrary = {}; // <- устаўлена + + })(window); //<- устаўлена + +> **Заўвага:** Парсер JavaScript не апрацоўвае "карэктна" аператар return, калі +> пасля яго пачынаецца новы радок. Магчыма прычына і не ў аўтаматычнай устаўцы +> кропак з коскай, але гэта, тым не менш, непажаданы эфект. + +Парсер кардынальна памяняў паводзіны кода. У пэўных выпадках ён прымае **памылковыя +рашэнні**. + +### Вядучыя дужкі + +У выпадку вядучай дужкі парсер **не** уставіць кропку з коскай. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Гэты код ператворыцца ў радок. + + log('testing!')(options.list || []).forEach(function(i) {}) + +**Вельмі** верагодна, што `log` **не** вяртае функцыю; Таму вышэйпрыведзены +код справакуе `TypeError` з заявай, што `undefined` не з'яўляецца функцыяй. + +### Заключэнне + +Крайне рэкамендуецца **ніколі** не прапускаць кропку з коскай. Таксама заўсёды +рэкамендуецца ставіць дужкі на той жа лініі, што і адпаведныя канструкцыі, і ніколі +не прапускаць іх у аднарадковых канструкцыях `if` / `else`. Гэтыя меры не толькі +павысяць кансістэнтнасць кода, але таксама прадухіляць ад таго, што парсер +JavaScript зменіць паводзіны кода. diff --git a/external/JavaScript-Garden/doc/by/core/undefined.md b/external/JavaScript-Garden/doc/by/core/undefined.md new file mode 100644 index 0000000..18af0f2 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/core/undefined.md @@ -0,0 +1,69 @@ +## `undefined` і `null` + +JavaScript мае два розныя значэнні для 'нічога' - гэта `null` і `undefined`, пры +гэтым апошняе больш карыснае. + +### Значэнне `undefined` + +`undefined` — гэта тып з роўна адным значэннем: `undefined`. + +Мова таксама аб'яўляе глабальную пераменную, што мае значэнне `undefined`; Гэта +пераменная таксама называецца `undefined`. Тым не менш, гэта пераменная, а **не** +канстанта, ці ключавое слова. Гэта азначае, што яе *значэнне* можа быць +з лёгкасцю перазапісаным. + +> **Заўвага для ES5:** у ECMAScript 5 `undefined` **больш** *нельга запісваць* у +> строгім рэжыме, але гэта імя можа ўся яшчэ быць перакрыта праз выкарыстанне +> функцыі з іменем `undefined`. + +Ніжэй пералічаныя некалькі выпадкаў, калі вяртаецца `undefined`: + + - Доступ да (немадыфікаванай) глабальнай пераменнай `undefined`. + - Доступ да аб'яўленай, *але яшчэ не* ініцыалізаванай пераменнай. + - Няяўна вернутае значэнне функцыі праз адсутнасць аператара `return`. + - З аператара `return`, які нічога яўна не вяртае. + - У выніку пошуку неіснуючай уласцівасці аб'екта. + - Параметры функцыі, якім яўна не было прысвоена значэнне. + - Усё, чаму было прысвоена значэнне `undefined`. + - Любы выраз у форме `void(expression)`. + +### Апрацоўка зменаў значэння `undefined` + +З той прычыны, што глабальная пераменная `undefined` утрымлівае толькі копію +актуальнага *значэння* `undefined`, прысвойванне ёй новага значэння **не** мяняе +значэнне *тыпа* `undefined`. + +Таксама для таго, каб параўнаць што-небудзь з значэннем `undefined`, спачатку +трэба атрымаць значэнне `undefined`. + +Звыклая тэхніка абароны ад магчымага перазапісвання пераменнай `undefined` — +дадатковы параметр у [ананімнай абгортцы](#function.scopes), што выкарыстоўвае +адсутны аргумент. + + var undefined = 123; + (function(something, foo, undefined) { + // цяпер undefined у лакальнай зоне бачнасці + // зноў спасылаецца на значэнне `undefined` + + })('Hello World', 42); + +Гэтага ж выніку можна дасягнуць праз аб'яўленне ўнутры абгорткі. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Адзіная розніца ў тым, што гэта апошняя версія будзе большай на 4 байты пры +мініфікацыі, а ў першым унутры ананімнай абгорткі не будзе аператара `var`. + +### Выкарыстоўванне `null` + +Хаця `undefined` у кантэксце мовы JavaScript у асноўным выкарыстоўваецца ў сэнсе +традыйнага *null*, сам `null` (і літэрал і тып) з'яўляецца яшчэ адным тыпам дадзеных. + +Выкарыстоўваецца ў некаторых унутранных механізмах JavaScript (напрыклад аб'яўленне +канца ланцужка прататыпаў пазначаючы `Foo.prototype = null`), але амаль што ва ўсіх +выпадках ён можа быць заменены на `undefined`. diff --git a/external/JavaScript-Garden/doc/by/function/arguments.md b/external/JavaScript-Garden/doc/by/function/arguments.md new file mode 100644 index 0000000..98ac3d2 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/arguments.md @@ -0,0 +1,130 @@ +## Аб'ект `arguments` + +У зоне бачнасці любой функцыі JavaScript ёсць доступ да адмысловай пераменнай +`arguments`. Гэтая пераменная утрымлівае спіс усіх аргументаў, што былі +перададзеныя функцыі. + +> **Заўвага:** У выпадку, калі `arguments` ужо быў створаны ўнутры зоны бачнасці +> функцыі праз выраз `var`, або з'яўляецца фармальным параметрам, +> аб'ект `arguments` не будзе створаны. + +Аб'ект `arguments` **не з'яўляецца** спадкаемцам `Array`. Ён мае падабенствы +з масівам, напрыклад уласцівасць `length`. Але ён не ўспадкоўвае `Array.prototype`, +а ўяўляе з сябе `Object`. + +Таму **не**магчыма выклікаць стандартныя метады `push`, `pop` або `slice` +у аб'екта `arguments`. Тым не менш, ітэрацыя з звычайным цыклам `for` працуе карэктна. +Неабходна канвертаваць яго ў сапраўдны аб'ект `Array`, каб прымяніць стандартныя +метады масіваў. + +### Канвертацыя ў масіў + +Ніжэйпрыведезны код верне новы масіў, які будзе ўтрымліваць усе элементы аб'екта +`arguments`. + + Array.prototype.slice.call(arguments); + +Такая канвертацыя **марудная**, яе **не рэкамендуецца** выкарыстоўваць у крытычных +у плане прадукцыйнасці частках кода. + +### Перадача `arguments` + +Ніжэй прадстаўлены рэкамендаваны спосаб перадачы аргументаў з адной функцыі ў іншую. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // тут робім што-небудзь + } + +Яшчэ адзін прыём — гэта выкарыстанне `call` і `apply` разам, каб ператварыць метады, +што выкарыстоўваюць значэнне `this` як і свае аргументы, у звычайныя функцыі, +што выкарыстоўваюць толькі аргументы. + + function Person(first, last) { + this.first = first; + this.last = last; + } + + Person.prototype.fullname = function(joiner, options) { + options = options || { order: "western" }; + var first = options.order === "western" ? this.first : this.last; + var last = options.order === "western" ? this.last : this.first; + return first + (joiner || " ") + last; + }; + + // Ствараем незвязаную версію "fullname", што можа быць выкарыстана з любым + // аб'ектам, які мае ўласцівасці 'first' і 'last', перададзеным у якасці + // першага параметра. Гэтую абгортку не трэба будзе мяняць, калі колькасць або + // парадак аргументаў fullname зменяцца. + Person.fullname = function() { + // Result: Person.prototype.fullname.call(this, joiner, ..., argN); + return Function.call.apply(Person.prototype.fullname, arguments); + }; + + var grace = new Person("Grace", "Hopper"); + + // 'Grace Hopper' + grace.fullname(); + + // 'Turing, Alan' + Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" }); + + +### Фармальныя параметры і індэксы аргументаў + +Аб'ект `arguments` стварае *гэтэр* і *сэтэр* як да кожнай са сваіх уласцівасцяў, +так і да фармальных параметраў функцыі. + +У выніку змена значэння фармальнага параметра зменіць таксама адпаведную ўласцівасць +аб'екта `arguments`, і наадварот. + + 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); + +### Міфы і праўда аб прадукцыйнасці + +Адзінае, калі `arguments` не ствараецца, — гэта калі ёсць фармальны аргумент функцыі +або пераменная ўнутры яе з такім іменем. Не важна, выкарыстоўваюцца яны ці не. + +Як *гэтэры*, так і *сэтэры* ствараюцца **заўсёды**, таму іх выкарыстоўванне не +мае амаль ніякага ўплыву на прадукцыйнасць. + +> **Заўвага для ES5:** *гэтэры* і *сэтэры* не ствараюцца ў строгім рэжыме. + +Тым не менш, ёсць адна рэч, якая можа жахліва знізіць прадукцыйнасць у сучасных +рухавіках JavaScript — гэта выкарыстанне `arguments.callee`. + + function foo() { + arguments.callee; // робім што-небудзь з функцыяй foo + arguments.callee.caller; // і з функцыяй, якая выклікала foo + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Звычайна ўстаўляецца... + } + } + +У вышэйпрыведзеным кодзе `foo` больш не можа быць [устаўлена][1], бо ёй трэба +ведаць аб сабе і аб функцыі, што яе выклікала. Гэта не толькі знішчае павышэнне +прадукцыйнасці, якое магло адбыцца дзякуючы ўстаўцы, але і парушае інкапсуляцыю, бо +функцыя цяпер залежыць ад спецыфічнага кантэксту, які яе выклікае. + +Выкарыстоўванне `arguments.callee` або яго ўласцівасцяў **вельмі непажадана**. + +> **Заўвага для ES5:** У строгім рэжыме `arguments.callee` кіне памылку `TypeError`, +> бо яго выкарыстанне аб'яўлена састарэлым. + +[1]: http://en.wikipedia.org/wiki/Inlining diff --git a/external/JavaScript-Garden/doc/by/function/closures.md b/external/JavaScript-Garden/doc/by/function/closures.md new file mode 100644 index 0000000..23911e6 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/closures.md @@ -0,0 +1,113 @@ +## Замыканні і спасылкі + +Адна з найбольш магутных магчымасцяў JavaScript — магчымасць ствараць *замыканні*. +Зона бачнасці замыканняў **заўсёды** мае доступ да знешняй зоны бачнасці, у якой +замыканне было аб'яўлена. З той прычыны, што ў JavaScript адзіны механізм працы +з зонай бачнасці — гэта [зоны бачнасці функцыі](#function.scopes), усе функцыі +выступаюць у якасці замыканняў. + +### Эмуляцыя прыватных пераменных + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Тут `Counter` вяртае **два** замыканні: функцыю `increment` і функцыю `get`. +Абедзьве функцыі маюць **спасылку** на зону бачнасці `Counter` і таму заўсёды +маюць доступ да пераменнай `count`, што была аб'яўлена ў гэтай зоне бачнасці. + +### Якім чынам гэта працуе + +З той прычыны, што ў JavaScript немагчыма спасылацца або прысвойваць зоны бачнасці, +**немагчыма** атрымаць доступ да пераменнай `count` звонку. Адзіны спосаб +узаемадзейнічаць з ім — выкарыстоўваць два замыканні. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +Вышэйпрыведзены код **не** памяняе значэнне пераменнай `count` у зоне бачнасці +`Counter`, бо `foo.hack` не быў аб'яўлены у **гэтай** зоне бачнасці. Замест гэтага +ён створыць або перазапіша *глабальную* пераменную `count`. + +### Замыканні ўнутры цыклаў + +Частая памылка - выкарыстанне замыканняў унутры цыклаў, як быццам бы яны капіруюць +значэнне пераменнай індэксу цыкла. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Вышэйпрыведзены код **не** выведзе нумары ад `0` да `9`, ён проста выведзе +нумар `10` дзесяць разоў. + +*Ананімная* функцыя захоўвае **спасылку** на `i`. У той час, калі функцыя `console.log` +выклікаецца, `цыкл for` ужо адпрацаваў, а значэнне `i` ўжо стала `10`. + +Каб атрымаць пажаданыя паводзіны, неабходна стварыць **копію** значэння `i`. + +### Як абыйсці праблемы спасылкі + +Каб стварыць копію значэння пераменнай індэкса цыкла, лепшы спосаб — стварэнне +[ананімнай абгорткі](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +Знешняя ананімная функцыя выконваецца імгненна з `i` ў якасці першага аргумента +і атрымае копію **значэння** `i` ў якасці параметра `e`. + +Ананімная функцыя, што перадаецца метаду `setTimeout`, цяпер мае спасылку на +`e`, чыё значэнне **не** мяняецца на працягу цыкла. + +Яшчэ адзін спосаб атрымаць такі вынік — вяртаць функцыю з ананімнай абгорткі, +што будзе паводзіць сябе такім жа чынам, як і папярэдні прыклад. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +Яшчэ адзін папулярны спосаб дасягнуць гэтага — дадаць яшчэ адзін аргумент выкліку +функцыі `setTimeout`, якая перадасць агрумент функцыі зваротнага выкліку. + + for(var i = 0; i < 10; i++) { + setTimeout(function(e) { + console.log(e); + }, 1000, i); + } + +Некаторыя старыя асяродкі JS (Internet Explorer 9 і ніжэй) не падтрымліваюць +гэтую магчымасць. + +Таксама магчыма выканаць гэта выкарыстоўваючы `.bind`, якая можа звязаць +`this` і аргументы функцыі. Ніжэйпрыведзены прыклад працуе як папярэднія. + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } diff --git a/external/JavaScript-Garden/doc/by/function/constructors.md b/external/JavaScript-Garden/doc/by/function/constructors.md new file mode 100644 index 0000000..1da70bf --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/constructors.md @@ -0,0 +1,122 @@ +## Канструктары + +Канструктары ў JavaScript таксама адрозніваюцца ад большасці іншых моваў. Любы +выклік функцыі, якому папярэднічае ключавое слова `new` з'яўляецца канструктарам. + +Унутры канструктара (выкліканай функцыі) - значэнне `this` спасылаецца на +новаствораны аб'ект. [Прататыпам](#object.prototype) **новага** +аб'екта прызначаецца `prototype` функцыі, што была выклікана ў якасці канструктара. + +У выпадку, калі выкліканая функцыя не вяртае яўнага значэння праз `return`, будзе +не яўна вернута значэнне `this`, то бок новы аб'ект. + + function Person(name) { + this.name = name; + } + + Person.prototype.logName = function() { + console.log(this.name); + }; + + var sean = new Person(); + +У гэтым прыкладзе `Person` выклікаецца ў якасці канструктара, адпаведна `prototype` +створанага аб'екта будзе прывязаны да `Person.prototype`. + +Вярнуць яўнае значэнне праз `return`, можна **толькі** калі гэта значэнне - `Object`. + + function Car() { + return 'ford'; + } + new Car(); // новы аб'ект, не 'ford' + + function Person() { + this.someValue = 2; + + return { + name: 'Charles' + }; + } + new Person(); // вяртае аб'ект ({name:'Charles'}), які не ўтрымлівае someValue + +Калі ключавое слова `new` прапушчана, функцыя **не** верне аб'ект. + + function Pirate() { + // пазначыць значэнне ў глабальным аб'екце! + this.hasEyePatch = true; + } + var somePirate = Pirate(); // somePirate == undefined + +Гэты прыклад можа спрацаваць у некаторых выпадках, праз тое як працуе +[`this`](#function.this) у JavaScript. Значэннем `this` тут будзе +*глабальны аб'ект*. + +### Фабрыкі + +Каб мець магчымасць прапусціць ключавое слова `new`, канструктар функцыі мае яўна +вяртаць значэнне. + + function Robot() { + var color = 'gray'; + return { + getColor: function() { + return color; + } + } + } + + new Robot(); + Robot(); + +Абодва выклікі `Robot` вернуць тое ж самае, новы аб'ект, які мае ўласцівасць +`getColor`, што з'яўляецца [замыканнем](#function.closures). + +Таксама варта адзначыць, што выклік `new Robot()` **не** ўплывае на прататып +вернутага аб'екта. Хаця прататып будзе прызначаны новастворанаму аб'екту, `Robot` +ніколі не верне гэты аб'ект. + +У прыкладзе вышэй, няма розніцы паміж выклікам функцыі з аператарам `new` або +без яго. + + +### Стварэнне новых аб'ектаў з выкарыстаннем фабрык + +Часта рэкамендуюць **не** выкарыстоўваць `new` бо забыўшыся выкарыстаць яго, можна +стварыць памылку. + +Каб стварыць новы аб'ект, лепш выкарыстоўваць фабрыку і стварыць новы аб'ект +*унутры фабрыкі*. + + function CarFactory() { + var car = {}; + car.owner = 'nobody'; + + var milesPerGallon = 2; + + car.setOwner = function(newOwner) { + this.owner = newOwner; + } + + car.getMPG = function() { + return milesPerGallon; + } + + return car; + } + +Хоць гэты прыклад і спрацуе негледзячы на забытае `new`, і бясспрэчна выкарыстоўвае +[прыватныя пераменныя](#function.closures), ён мае некалькі недахопаў. + + 1. Ён выкарыстоўвае больш памяці, бо функцыі створаных аб'ектаў **не** захоўваюццца + у прататыпе, а ствараюцца на нова для кожнага аб'екта. + 2. Каб эмуляваць спадкаемства, фабрыка мае скапіраваць метады іншага аб'екта, або + пазначыць прататыпам новага аб'екта стары. + 3. Разрыў ланцужка прататыпаў, проста па прычыне забытага ключавога слова `new`, + не адпавядае духу мовы JavaScript. + +### У заключэнне + +Негледзячы на тое, што прапушчанае `new` можа выліцца ў памылку, гэта **не** +прычына адмовіцца ад выкарыстання прататыпаў. У выніку лепш высвятліць якое рашэнне +больш адпавядае патрабаванням праграмы. Асабліва важна выбраць пэўны стыль і +**паслядоўна** выкарыстоўваць яго. diff --git a/external/JavaScript-Garden/doc/by/function/general.md b/external/JavaScript-Garden/doc/by/function/general.md new file mode 100644 index 0000000..da5f4ba --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/general.md @@ -0,0 +1,48 @@ +## Выразы і аб'яўленне функцый + +У JavaScript функцыі таксама з'яўляюцца аб'ектамі. Гэта значыць іх можна перадаваць +і прысвойваць як і любыя іншыя аб'екты. Адзін, часта выкарыстоўваемы варыянт, +гэтай магчымасці - перадача *ананімнага метада* як функцыі зваротнага выкліку +іншай, магчыма асінхроннай функцыі. + +### Аб'яўленне `function` + + function foo() {} + +У вышэй прыведзеным прыкладзе функцыя [уздымаецца](#function.scopes) перад тым +як пачынаецца выконванне праграмы; Такім чынам, яна даступная *паўсюль* у зоне +бачнасці, у якой яна была *аб'яўлена*, нават калі выклік адбываецца да фактычнага +аб'яўлення ў кодзе. + + foo(); // Працуе, бо функцыя будзе створана да выконвання кода + function foo() {} + +### `function` як выраз + + var foo = function() {}; + +У гэтым прыкладзе пераменнай `foo` прысвойваецца *ананімная* функцыя. + + foo; // 'undefined' + foo(); // уздыме TypeError + var foo = function() {}; + +Праз тое, што `var` - гэта аб'яўленне якое уздымае імя пераменнай `foo` перад тым +як код будзе выкананы, `foo` будзе ўжо аб'яўленым калі ён пачне выконвацца. + +Але так як прысвойванні адбываюцца толькі пад час выконвання, значэнне `foo` +будзе змоўчанным ([undefined](#core.undefined)) да выконвання адпаведнага кода. + +### Выразы з іменаванымі функцыямі + +Яшчэ адзін выбітны выпадак - прысвойванне іменавай функцыі. + + var foo = function bar() { + bar(); // працуе + } + bar(); // ReferenceError + +Тут, `bar` не даступны ў знешнім скоўпе, бо функцыя толькі прысвойваецца пераменнай +`foo`; аднак, унутры `bar`, імя даступнае. Так адбываецца праз асаблівасці працы +з [прастранствамі імён](#function.scopes) у JavaScript - імя функцыі *заўсёды* +даступнае ў лакальным скоўпе функцыі. diff --git a/external/JavaScript-Garden/doc/by/function/scopes.md b/external/JavaScript-Garden/doc/by/function/scopes.md new file mode 100644 index 0000000..7ade2cf --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/scopes.md @@ -0,0 +1,230 @@ +## Зоны бачнасці і прасторы імёнаў + +Негледзячы на тое, што JavaScript добра працуе з сінтаксісам фігурных дужак для +блокаў, у ім **няма** падтрымкі блочнай зоны бачнасці; усё што ёсць на гэты конт +у мове - *зона бачнасці функцыі*. + + function test() { // зона бачнасці + for(var i = 0; i < 10; i++) { // не зона бачнасці + // лічым + } + console.log(i); // 10 + } + +> **Заўвага:** Калі не выкарыстана ў прысвойванні, аператары return або аргуменце +> функцыі, натацыя `{...}` будзе інтэрпрэтавана як блочны выраз, а **не** як літэрал +> аб'екта. Гэта з'ява, сумесна з [аўтаматычнай устаўкай коскі з кропкай](#core.semicolon), +> можа прывесці да хітрых памылак. + +Таксама JavaScript не падтрымлівае выразныя прасторы імёнаў, усё аб'яўляецца ў +агульнадаступнай прасторы імёнаў. + +Для кожнай спасылкі на пераменную, JavaScript пойдзе ўверх па ўсіх зонах бачнасці, +пакуль не знойдзе яе. У выпадку, калі ён дойдзе да глабальнай прасторы імён і ўсё +яшчэ не знойдзе неабходнае імя, ён уздыме `ReferenceError`. + +### Атрута глабальнымі пераменнымі + + // скрыпт A + foo = '42'; + + // скрыпт B + var foo = '42' + +Вышэйпрыведзеныя скрыпты маюць **розныя** вынікі. Скрыпт A аб'яўляе пераменную +`foo` у *глабальнай* зоне бачнасці, скрыпт B аб'яўляе `foo` у *актуальнай* зоне +бачнасці. + +Паўторымся, гэта абсалютна **не** *той жа самы вынік*: не выкарыстоўваенне `var` +можа мець сур'ёзныя наступствы. + + // глабальная зона бачнасці + var foo = 42; + function test() { + // лакальная зона бачнасці + foo = 21; + } + test(); + foo; // 21 + +З-за таго, што аператар `var` прапушчаны ўнутры функцыі `test`, значэнне `foo` +у глабальнай прасторы імён будзе перазапісаным. Хаця першапачаткова гэта можа +падацца невялікай праблемай, не выкарыстоўванне `var` у кодзе на тысячы радкоў, +прывядзе да жахлівых, цяжкіх для адладкі памылак. + + // глабальная прастора імёнаў + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // прастора імёнаў subLoop + for(i = 0; i < 10; i++) { // аператар var прапушчаны + // робім чароўныя рэчы! + } + } + +Знешні цыкл скончыцца пасля першага выкліка `subLoop`, бо `subLoop` перазапісвае +глабальную пераменную `i`. Выкарыстоўваючы `var` для другога цыкла `for` можна +было б пазбегнуць памылкі. **Ніколі** не прапускайце аператар `var`, акрамя +выпадкаў, калі змена дадзеных у знешняй зоне бачнасці ёсць *пажаданым вынікам*. + +### Лакальныя пераменныя + +Адзіная крыніца лакальных пераменных у JavaScript гэта параметры [функцыі](#function.general) +і пераменныя аб'яўленыя праз аператар `var`. + + // глабальная зона бачнасці + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // лакальная зона бачнасці функцыі test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` і `i` гэта лакальныя пераменныя унутры зоны бачнасці функцыі `test`, +а вось прызначэнне `bar` перазапіша глабальныю пераменную з тым жа іменем. + +### Падыманне + +JavaScript **падымае** аб'яўленні. Гэта азначае, што абодва аб'яўленні аператараў +`var` і `function` падымуцца на верх іх зоны бачнасці. + + 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]; + } + } + +Вышэйпрыведзены код трансфармуецца перад пачаткам выконвання. JavaScript падымае +аператары `var`, як і аб'яўленне `function`, наверх бліжэйшай зоны бачнасці. + + // аператар var перамяшчаецца сюды + var bar, someValue; // па змоўчванню - 'undefined' + + // аб'яўленне функцыі таксама падымаецца наверх + function test(data) { + var goo, i, e; // адсутная блочная зона бачнасці перайшла сюды + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // падае з TypeError бо ўсё яшчэ 'undefined' + someValue = 42; // прысвойванні не падымаюцца + bar = function() {}; + + test(); + +Адсутнасць блочнай зоны бачнасці не толькі падыме аператар `var` па-за межы цыкла +і яго цела, але таскама зробіць вынік некаторых канструкцый `if` не-інтуітыўным. + +Хоць у арыгінальным кодзе падаецца што канструкцыя `if` змяняе *глабальную +пераменную* `goo`, на дадзены момант гэта мяняе *лакальную пераменную* - пасля +таго, як было прыменена падыманне. + +Без ведаў аб *падыманні*, можна падумаць што код ніжэй кіне `ReferenceError`. + + // правярае ці было SomeImportantThing праініцыалізавана + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Але канешне, гэта працуе праз тое, што аператар `var` быў падняты на верх +глабальнай *зоны бачнасці*. + + var SomeImportantThing; + + // тут нейкі код можа ініцыалізаваць SomeImportantThing, або не + + // тут у гэтым можна ўпэўніцца + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Парадак доступу да пераменных + +Усе зоны бачнасці ў JavaScript, уключаючы *глабальную зону бачнасці*, маюць +адмысловае імя [`this`](#function.this), аб'яўленае ўнутры іх, якое спасылаецца +на *актуальны аб'ект*. + +Зоны бачнасці функцый таксама маюць імя [`arguments`](#function.arguments), +аб'яўленае ў іх, якое спасылаецца на аргументы, што былі перададзеныя ў функцыю. + +Напрыклад, калі паспрабаваць атрымаць доступ да пераменнай `foo` унутры зоны +бачнасці функцыі, JavaScript будзе шукаць імя ў наступным парадку: + + 1. У выпадку калі прысутнічае канструкцыя `var foo` у актуальнай зоне бачнасці, + ёна і выкарыстоўваецца. + 2. Калі параметр функцыі мае імя `foo`, ён будзе выкарыстаны. + 3. Калі сама функцыя называецца `foo`, яна будзе выкарыстана. + 4. Пераходзіць у знешнюю зону бачнасці, і пачынае з пункта **#1**. + +> **Заўвага:** Наява параметра названага `arguments` **перадухіліць** стварэнне +> параметра `arguments` па змоўчванні. + +### Прасторы імёнаў + +Вялікая праблема, звязаная з выкарыстоўваннем глабальнай прасторы імёнаў, гэта +высокая верагоднасць перасячэння імёнаў пераменных. У JavaScript, гэта праблема +можа быць лёгка пазбегнута праз выкарыстанне *ананімных абгортак*. + + (function() { + // аўтаномная "прастора імён" + + window.foo = function() { + // адкрытае замыканне + }; + + })(); // імгненнае выкананне функцыі + + +Ананімныя функцыі з'яўляюцца [выразамі](#function.general); таму каб быць выкліканымі, +яны спачатку маюць быць ацэненымі. + + ( // ацэньваем функцыю ўнутры дужак + function() {} + ) // вяртаем аб'ект функцыі + () // выклік выніку ацэнкі + +Ёсць і іншыя спосабы ацаніць і імгненна выклікаць выраз функцыі, хаця яны і +адрозніваюцца па сінтаксісу, паводзяць сябе аднолькава. + + // Яшчэ некалькі спосабаў наўпрост выклікаць функцыю + !function(){}() + +function(){}() + (function(){}()); + // і так далей... + +### Заключэнне + +Рэкамендуецца заўсёды выкарыстоўваць *ананімную абгортку* каб інкапсуліраваць код +у яго асабістай прасторы імёнаў. Гэта не толькі абараняе код ад перасячэння імёнаў, +але і дапамагае падзяляць праграму на модулі. + +Таксама выкарыстанне глабальных пераменных лічыцца **дрэннай практыкай**. **Любое** +іх выкарыстоўванне - прыкмета дрэнна напісанага кода, схільнага да памылак, і цяжага +ў падтрымцы. diff --git a/external/JavaScript-Garden/doc/by/function/this.md b/external/JavaScript-Garden/doc/by/function/this.md new file mode 100644 index 0000000..0a85ef5 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/function/this.md @@ -0,0 +1,118 @@ +## Як працуе `this` + +У JavaScript `this` азначае канцэптуальна іншую рэч, чым у іншых мовах праграмавання. +Існуе роўна **пяць** варыянтаў таго, да чаго можа быць прывязана `this`. + +### Глабальна зона бачнасці + + this; + +Калі `this` выкарыстоўваецца ў глабальнай зоне бачнасці - яна спасылаецца на *глабальны* аб'ект + + +### Выклік функцыі + + foo(); + +Тут, `this` усё яшчэ спасылаецца на *глабальны* аб'ект. + +> **Заўвага для ES5:** У строгім рэжыме, паняцця глабальнасці **больш не існуе**. +> `this` у такім выпадку будзе мець значэнне `undefined`. + +### Выклік метада + + test.foo(); + +У гэтым выпадку, `this` будзе спасылацца на аб'ект `test`. + +### Выклік канструктара + + new foo(); + +Выклік функцыі, перад якім прысутнічае ключавое слова `new`, выступае ў якасці +[канструктара](#function.constructors). Унутры функцыі, `this` будзе спасылацца +на *новаствораны* аб'ект. + +### Яўная ўстаноўка `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // масіў разгорнецца ў ніжэйпрыведзенае + foo.call(bar, 1, 2, 3); // вынікам будзе a = 1, b = 2, c = 3 + +Пры выкарыстоўванні метадаў `call` або `apply` з `Function.prototype`, значэнню +`this` унутры выкліканай функцыі **яўна прысвойваецца** значэнне першага +аргумента адпаведнага выкліка функцыі. + +Як вынік, у вышэйпрыведзеным прыкладзе *правіла метада* **не** працуе, і `this` +унутры `foo` будзе мець значэнне `bar`. + +> **Заўвага:** `this` **не можа** быць выкарыстаны як спасылка ўнутры літэрала +> `Object`. Таму ў кодзе `var obj = {me: this}` `me` **не** будзе спасылацца на +> `obj`, бо `this` прывязваецца толькі па пяці вышэйапісаных правілах. + +### Магчымыя пасткі + +Не гледзячы на тое, што большасць гэтых прыкладаў лагічныя, першы можна лічыць +яшчэ адным недаглядам мовы, бо ён **ніколі** не мае практычнага прымянення. + + Foo.method = function() { + function test() { + // this спасылаецца на глабальны аб'ект + } + test(); + }; + +Памылковым меркаваннем будзе тое, што `this` унутры `test` будзе спасылацца на +`Foo`; Але на самрэч **гэта не так**. + +Каб атрымаць доступ да `Foo` з цела `test`, вы можаце стварыць лакальную +пераменную унутры `метада` што будзе спасылацца на `Foo`. + + Foo.method = function() { + var self = this; + function test() { + // Тут выкарыстоўвайце self замест this + } + test(); + }; + +`self` гэта звычайнае імя пераменнай, але яно часта выкарыстоўваецца для спасылкі +на знешні `this`. У камбінацыі з [замыканнямі](#function.closures), яно можа быць +выкарыстана для перадачы `this` навокал. + +У ECMAScript 5 можна выкарыстаць метад `bind` у камбінацыі з ананімнай функцыяй, +дзеля таго каб атрымаць аналагічны вынік. + + Foo.method = function() { + var test = function() { + // this цяпер спасылаецца на Foo + }.bind(this); + test(); + }; + +### Прысвойванне метадаў + +Яшчэ адна рэч якая **не** працуе ў JavaScript - гэта стварэнне псэўданімаў функцый, +то бок **прысвойванне** значэння метада пераменнай. + + var test = someObject.methodTest; + test(); + +Паводле першага правіла, `test` цяпер працуе як звычайны выклік функцыі; +адпаведна, `this` унутры больш не будзе спасылацца на `someObject`. + +Поздняе звязванне `this` можа падацца дрэннай ідэяй, але насамрэч якраз дзякуючы +гэтаму працуе [спадкаемства прататыпаў](#object.prototype). + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Калі выклікаецца `method` новага экзэмпляра `Bar`, `this` будзе спасылацца на гэты +экзэмпляр. diff --git a/external/JavaScript-Garden/doc/by/index.json b/external/JavaScript-Garden/doc/by/index.json new file mode 100644 index 0000000..1ed0aba --- /dev/null +++ b/external/JavaScript-Garden/doc/by/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden па Беларуску", + "description": "Мануал па недахопаў і дзівосах JavaScript", + "sections": [ + { + "title": "Уводзіны", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Аб'екты", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "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" + ] + }, + { + "title": "Ядро", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Рэшта", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/by/intro/index.md b/external/JavaScript-Garden/doc/by/intro/index.md new file mode 100644 index 0000000..ea82a31 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/intro/index.md @@ -0,0 +1,56 @@ +## Уступ + +**JavaScript Garden** гэта растучы набор дакументацыі аб найбольш +цікавых частках мовы праграмавання JavaScript. Ён дае парады аб тым як прадухіліць +частыя і непрадказальныя памылкі, а таксама праблемы з хуткасцю выконвання і +дрэннымі практыкамі, якія праграмісты, не з'яўляючыяся экспертамі у JavaScript +маглі сустрэць падчас сваіх пошукаў у глыбіні мовы. + +JavaScript Garden **не** ставіць сваёй мэтай навучыць вас мове JavaScript. Былыя +веды мовы рэкамендаваныя, каб вы змаглі зразумець пытанні разглядаемыя ў гэтым +мануале. Каб зразумець базавыя рэчы мовы, калі ласка прачытайце цудоўны [мануал][1] +у сетцы распрацоўшчыкаў Mozilla. + +## Аўтары + +Гэты мануал - праца двух выбітных карыстальнікаў [Stack Overflow][2] , [Ivo Wetzel][3] +(Тэкст) і [Zhang Yi Jiang][4] (Дызайн). + +На дадзены момант падтрымліваецца [Tim Ruffles](http://truffles.me.uk). + +## Удзельнікі + +- Зашмат каб пералічыць тут, [паглядзіце спіс](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + + +## Хостынг + +JavaScript Garden хосціцца на GitHub, але [Cramer Development][7] падтрымлівае нас +люстэркам на [JavaScriptGarden.info][8]. У Беларускамоўнай версіі таксама ёсць +сваё [люстэрка на GitHub][13] + +## Пераклад + +Перакладзена на Беларускую мову супольнасцю [it-mova][14]. + +## Ліцэнзія + +JavaScript Garden апублікаваны пад [MIT ліцэнзіяй][9] і хосціцца на +[GitHub][10]. Калі вы знойдзеце апячатку або памылку - [пазначце памылку][11] або +адпраўце pull request у сховішча. Вы таксама можаце знайсці нас у +[JavaScript room][12] на чаце Stack Overflow. + +[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/it-na-move/JavaScript-Garden/ +[11]: https://github.com/it-na-move/JavaScript-Garden/issues +[12]: http://chat.stackoverflow.com/rooms/17/javascript +[13]: http://it-na-move.github.io/JavaScript-Garden/ +[14]: https://github.com/it-na-move diff --git a/external/JavaScript-Garden/doc/by/object/forinloop.md b/external/JavaScript-Garden/doc/by/object/forinloop.md new file mode 100644 index 0000000..fbe52f1 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/object/forinloop.md @@ -0,0 +1,63 @@ +## Цыкл `for in` + +Як і аператар `in`, цыкл `for in` праходзіць па ўсім ланцужку прататыпаў +пры ітэрацыі па ўласцівасцях аб'екта. + +> **Заўвага:** Цыкл `for in` **не** ітэруе па уласцівасцях, у якіх атрыбут +> `enumerable` мае значэнне `false`; напрыклад, уласцівасць `length` +> масіва (Array). + + // Атруцім Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // вывядзе і bar і moo + } + +Праз тое, што немагчыма памяняць паводзіны самаго цыкла `for in`, неабходна фільтраваць +непажаданыя ўласцівасці аб'екта ўнутры цыкла. У версіях ECMAScript 3 і пазней, +гэта можна зрабіць праз метад [`hasOwnProperty`](#object.hasownproperty). + +Пачынаючы з ECMAScript 5, `Object.defineProperty` можа быць выкарыстана з +`enumerable` роўным `false`, каб дадаць уласцівасці аб'екту такім чынам, што яны +не будуць пералічаны. У такім выпадку было б справядлівым меркаваць, што любая +`enumerable` уласціваць была дададзена адмыслова і прапусціць `hasOwnProperty`, бо +гэта робіць код больш шматслоўным і цяжэйшым для чытання. У бібліятэчным кодзе +`hasOwnProperty` мае быць усё роўна выкарыстаны, бо не варта рабіць здагадкі аб +тым, якія `enumerable` уласцівасці могуць пражываць у ланцужку прататыпаў. + +> **Заўвага:** праз тое, што `for in` заўсёды праходзіць праз увесць ланцужок прататыпаў, +> ён будзе працаваць усё марудней з кожным новым слоем спадкаемства. + +### Выкарыстоўванне `hasOwnProperty` для фільтрацыі + + // возьмем foo з прыкладу + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Гэта адзіная правільная версія выкарыстоўвання цыкла. Дзякуючы выкарыстоўванню +`hasOwnProperty`, быдзе выведзена **толькі** `moo`. +Калі прыбраць `hasOwnProperty`, код будзе схільны да памылак у выпадку, калі +натыўныя прататыпы — напрыклад, `Object.prototype` — былі змененыя. + +У новых версіях ECMAScript уласцівасці, пазначаныя як не enumerable, могуць быць +вызначыныя праз `Object.defineProperty`, змяншаючы рызыку ітэрацыі праз іх без +выкарыстання `hasOwnProperty`. Тым не менш, трэба быць уважлівым пры выкарыстанні +старых бібліятэк, такіх як [Prototype][1], якая не выкарыстроўвае новымя магчымасці +ECMAScript. +Пры выкарыстоўванні гэтай бібліятэкі, цыклы `for in`, якія не выкарыстоўваюць +`hasOwnProperty`, гарантавана не будуць працаваць. + +### У заключэнне + +Рэкамендавана **заўсёды** выкарыстоўваць `hasOwnProperty` як у ECMAScript 3 або +ніжэй, так і ў бібліятэчным кодзе. У гэтых асяродках ніколі не варта рабіць +здагадкі аб тым, быў зменены натыўны прататып ці не. Пачынаючы з ECMAScript 5, +`Object.defineProperty` дазваляе пазначаць уласцівасці як не `enumerable` і прапускаць +выкарыстоўванне `hasOwnProperty` у кодзе праграмы. + +[1]: http://www.prototypejs.org/ diff --git a/external/JavaScript-Garden/doc/by/object/general.md b/external/JavaScript-Garden/doc/by/object/general.md new file mode 100644 index 0000000..6977d8a --- /dev/null +++ b/external/JavaScript-Garden/doc/by/object/general.md @@ -0,0 +1,98 @@ +## Выкарыстанне і ўласцівасці аб'ектаў + +Усё ў JavaScript дзейнічае як аб'ект, апроч двух выключэнняў — гэта +[`null`](#core.undefined) і [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Часта распрацоўшчыкі думаюць, што лічбавыя літэралы не могуць быць выкарыстаны як +аб'екты. Гэта праз тое, што сінтаксічны аналізатар JavaScript стараецца прывесці +*натацыю кропка* пасля нумара да літэрала з плаваючай кропкай. + + 2.toString(); // уздымае SyntaxError + +Ёсць некалькі падыходаў, якія могуць дазволіць выкарыстаць лікавыя літэралы як +аб'екты'. + + 2..toString(); // другая кропка распазнаецца слушна + 2 .toString(); // заўважце прабел з лева ад кропкі + (2).toString(); // 2 распазнаецца першым чынам + +### Аб'ект як тып дадзеных + +Аб'екты ў JavaScript таксама могуць быць выкарыстаныя як [*хэш-табліцы*][1]; яны ў асноўным +складаюцца з іменаваных уласцівасцяў з адпаведнымі значэннямі. + +Выкарыстоўваючы натацыю літэрала аб'екта — `{}` — магчыма стварыць +просты аб'ект. Гэты новы аб'ект [пашырае](#object.prototype) `Object.prototype` і +не мае [сваіх уласцівасцяў](#object.hasownproperty) якія былі б вызначыныя. + + var foo = {}; // новы пусты аб'ект + + // новы аб'ект з уласціваццю 'test', якая мае значэнне 12 + var bar = {test: 12}; + +### Доступ да ўласцівасцяў + +Доступ да ўласцівасцяў аб'екта можа быць здейснены двумя спосабамі, праз кропкавую +натацыю або натацыю з квадратнымі дужкамі. + + var foo = {name: 'кацяня'} + foo.name; // кацяня + foo['name']; // кацяня + + var get = 'name'; + foo[get]; // кацяня + + foo.1234; // SyntaxError + foo['1234']; // працуе + +Натацыі працуюць амаль што ідэнтычна, з адзінай розніцай у тым, што +натацыя з квадратнымі дужкамі дазваляе дынамічную устаноўку ўласцівасцяў і +выкарыстанне імёнаў уласцівасцяў, якія інакш прывялі б да сінтаксічных памылак. + +### Выдаленне ўласцівасцяў + +Адзіны спосаб выдаліць уласціваць з аб'екта — гэта выкарыстаць аператар `delete`; +пазначэнне уласціваці як `undefined` або `null` толькі прыбірае +*значэнне* звязанае з уласцівацю, але не *ключ*. + + 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]); + } + } + +Вышэй прыведзены код вывядзе `bar undefined` і `foo null` — толькі `baz` быў +выдалены і таму адсутнічае ў вывадзе. + +### Натацыя ключэй + + var test = { + 'case': 'Я ключавое слова, таму я павінна быць пазначана як радок', + delete: 'Я таксама ключавое слова, таму і я' // уздымае SyntaxError + }; + +Уласцівасці аб'ектаў могуць быць пазначаныя як сімваламі, так і ў выглядзе радкоў. +Праз яшчэ адну хібу сінтаксічнага аналізатара JavaScript, вышэй прыведзены код +кіне `SyntaxError` у весіях ранейшых за ECMAScript 5. + +Гэта памылка ўздымаецца праз тое, што `delete` - гэта *ключавое слова*; такім чынам, +яно мае быць пазначана як *літэрал радка* каб забяспечыць, што яно будзе какрэктна +інтэрпрэтавана старымі рухавікамі JavaScript. + +[1]: http://en.wikipedia.org/wiki/Hashmap diff --git a/external/JavaScript-Garden/doc/by/object/hasownproperty.md b/external/JavaScript-Garden/doc/by/object/hasownproperty.md new file mode 100644 index 0000000..730ce94 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/object/hasownproperty.md @@ -0,0 +1,55 @@ +## Метад `hasOwnProperty` + +Каб праверыць, ці ёсць у аб'екта ўласцівасць, вызначаная ў *ім самім*, а не дзе-небудзь +у яго [ланцужку прататыпаў](#object.prototype), неабходна выкарыстаць метад +`hasOwnProperty`, які ўсе аб'екты ўспадкоўваюць ад `Object.prototype`. + +> **Заўвага:** **недастаткова** праверыць, ці значэнне уласцівасці — `undefined`. +> Уласцівасць можа існаваць, але яе значэнне было пазначана як `undefined`. + +`hasOwnProperty` — адзіная функцыя ў JavaScript, якая дазваляе атрымаць уласцівасці +аб'екта **без** зварота да ланцужка прататыпаў. + + // Сапсуем 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 + +Толькі `hasOwnProperty` дасць правільны чаканы вынік. Паглядзіце секцыю аб +[цыкле `for in`](#object.forinloop) для падрабязнейшых звестак аб тым, як +выкарыстоўваць `hasOwnProperty` падчас ітэрацыі па ўласцівасцях аб'екта. + +### `hasOwnProperty` як уласцівасць + +JavaScript не абараняе ўласцівасць `hasOwnProperty`; такім чынам, ёсць верагоднасць +што ў аб'екта можа быць уласцівасць з такім імем, неабходна выкарыстаць +*знешні* `hasOwnProperty` для карэктнага выніку. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Тут жывуць драконы' + }; + + foo.hasOwnProperty('bar'); // заўсёды верне false + + // выкарыстайце hasOwnProperty іншага аб'екта + // і перадайце foo у якасці this + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // Такасама магчыма выкарыстаць hasOwnProperty з Object.prototype + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + + +### Заключэнне + +Выкарыстоўванне `hasOwnProperty` ёсць **адзіным** надзейным спосабам, каб +праверыць існаванне ўласцівасці ў аб'екце. Рэкамендуецца выкарыстоўваць +`hasOwnProperty` пры ітэрацыі па ўласцівасцях аб'екта, як апісана ў секцыі +[цыкла `for in` ](#object.forinloop). diff --git a/external/JavaScript-Garden/doc/by/object/prototype.md b/external/JavaScript-Garden/doc/by/object/prototype.md new file mode 100644 index 0000000..ec80e80 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/object/prototype.md @@ -0,0 +1,115 @@ +## Прататып + +JavaScript не прадастаўляе класічную мадэль спадкаемства; замест гэтага, ён +выкарыстоўвае *прататыпную* мадэль. + +Негледзячы на тое, што гэта лічыцца адной з слабасцяў JavaScript, мадэль +прататыпнага спадкаемства больш эфэктыўная за класічную. +Напрыклад, даволі трывіальна пабудаваць класічную мадэль паверх прататыпнай мадэлі, +у той час як адваротнае было б значна больш складаным. + +JavaScript гэта адзіная шырока выкарыстоўваемая мова, якая падтрымлівае +прататыпнае спадкаемства, таму можа спатрэбіцца час, каб прызвычаіцца да гэтай мадэлі. + +Першая вялікая розніца заключаецца ў тым, што JavaScript выкастроўвае *прататыпныя +ланужкі*. + +> **Заўвага:** простае выкарыстоўванне `Bar.prototype = Foo.prototype` прывядзе +> да таго, што абодва аб'екта будуць выкарыстоўваць **той жа** прататып. +> Такім чынам, змены ў прататыпе любога з аб'ектаў паўплываюць на прататып іншага, +> што звычайна не ёсць пажаданым эфэктам. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Пазначае прататыпам Bar новы асобнік Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Упэнімся, што Bar з'яўляецца дзейсным канструктарам + Bar.prototype.constructor = Bar; + + var test = new Bar(); // стварае новы асобнік Bar + + // Выніковы ланцужок прататыпаў + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* і г.д. */ } + +У вышэй прыведзеным кодзе аб'ект `test` атрымае спадчыну і ад `Bar.prototype`, +і ад `Foo.prototype`; такім чынам, ён будзе мець доступ да функцыі `method`, якая +вызначана ў `Foo`. А таксама доступ да ўласцівасці `value` +**аднаго унікальнага асобніка** `Foo`, які з'яўляецца яго прататыпам. Важна заўважыць, +што `new Bar()` **не** стварае новы асобнік `Foo`, але выкарыстоўвае функцыю, +пазначаную яго прататыпам; такім чынам, усе асобнікі `Bar` будуць выкарыстоўваць +**тую ж** уласціваць `value`. + +> **Заўвага:** **не** выкарыстоўвайце `Bar.prototype = Foo`, бо гэта будзе +> ўказваць не на прататып `Foo`, а на аб'ект фукцыі `Foo`. Таму ланцужок +> прататыпаў будзе праходзіць праз `Function.prototype`, а не `Foo.prototype`; +> па гэтай прычыне `method` не будзе прысутнічаць у ланцужку прататыпаў. + +### Пошук уласцівасцяў + +Калі адбываецца зварот да ўласцівасці, JavaScript пройдзе па ўсім +ланцужку прататыпаў **уверх** да таго моманту, як знойдзе ўласціваць з запытаным імем. + +У той момант, калі дасягнуты верх ланцужка - а менавіта `Object.prototype` - і ўсё яшчэ +не знойдзена адпаведная ўласцівасць, будзе вернута значэнне [undefined](#core.undefined). + +### Уласцівасць prototype + +Нягледзячы на тое, што ўласцівасць prototype выкарыстоўваецца мовай, каб пабудаваць +ланцужок прататыпаў, магчыма прызначыць яму **любое** значэнне. Аднак, прызначэнне +прымітываў будузе праігнараваным. + + function Foo() {} + Foo.prototype = 1; // без эфекту + +Прызначэнне аб'ектаў, як паказана ў прыкладзе вышэй, будзе працаваць, і дазволіць +дынамічна ствараць ланцужкі прататыпаў. + +### Хуткасць выканання + +Пошук уласцівасцяў, якія знаходзяцца высока ў ланцужку прататыпаў, можа +негатыўна адбіцца на хуткасці выканання, і гэта можа быць прыкметным у кодзе, у якім +чыннік хуткасці крытычны. У выпадку спробы доступа да неіснуючых уласцівасцяў +будзе пройдзены ўвесь ланцужок прататыпаў. + +У дадатак, пры [ітэрацыі](#object.forinloop) па ўласцівасцях аб'екта +**кожная** уласціваць, што ёсць у ланцужку прататыпаў будзе апрацавана. + +### Расшырэнне ўбудаваных прататыпаў + +Адна з дрэнных магчымасцяў, што сустракаецца даволі часта — расшырэнне прататыпа +`Object.prototype` або аднаго з іншых убудаваных тыпаў. + +Такая практыка называецца [monkey patching][1] і парушае *інкапсуляцыю*. Хаця +папулярныя фрэймворкі, такія як [Prototype][2] шырока выкарыстоўваюць гэтую +мачымасць, няма добрых матываў для нагрувашчвання ўбудаваных тыпаў дадатковай +*нестандартнай* функцыянальнасцю. + +**Адзіным** добрым матывам расшырэння убудаваных прататыпаў — гэта дадаванне функцыянала, +што з'явіўся у новых рухавіках JavaScript; напрыклад, [`Array.forEach`][3]. + +### У завяршэнне + +**Вельмі важна** разумець, як працуе мадэль прататыпнага спадкаемства да таго, як +пісаць код, які яе выкарыстоўвае. Таксама сачыце за даўжынёй ланцужка прататыпаў +і драбіце іх, калі ёсць магчымасць, каб пазбегнуць праблем з прадукцыйнасцю. +Таксама ўбудаваныя прататыпы **ніколі** не павінны расшырацца, акрамя як для +таго, каб падтрымаць новыя магчымасці JavaScript. + +[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/external/JavaScript-Garden/doc/by/other/timeouts.md b/external/JavaScript-Garden/doc/by/other/timeouts.md new file mode 100644 index 0000000..1ed49dc --- /dev/null +++ b/external/JavaScript-Garden/doc/by/other/timeouts.md @@ -0,0 +1,168 @@ +### `setTimeout` і `setInterval` + +Дзякуючы асінхроннасці JavaScript магчыма запланаваць выкананне функцыі з дапамогай +метадаў `setTimeout` і `setInterval`. + +> **Заўвага:** Таймаўты **не** з'яўляюцца часткай стандарта ECMAScript. Яны былі +> распрацаваны ў [BOM, ці DOM Узровень 0][1], якія ніколі не былі аб'яўлены ці +> задакументаваны фармальна. Да гэтага часу няма рэкамендацыйных спецыфікацый, +> аднак, у дадзены момант яны стандартызаваны ў [HTML5][2]. У сувязі з гэтым +> рэалізацыя можа адрознівацца ў залежнасці ад браўзера і рухавіка + + function foo() {} + var id = setTimeout(foo, 1000); // вяртае Number > 0 + +Калі функцыя `setTimeout` выклікана, яна вяртае ID таймаўта і плануе выкананне +`foo` **прыблізна** праз тысячу мілісекунд. +`foo` будзе выканана **аднойчы**. + +Улічваючы вырашэнні таймера рухавіка Javascript, які выконвае код, +аднапаточнасць JavaScript і тое, што іншы код, які выконваецца +можа блакаваць паток, **нельга** быць упэўненым, што вы атрымаеце затрымку, +пазначанаю ў выкліку `setTimeout`. + +Функцыя, якая была перададзена як першы параметр, будзе выклікана +*глабальным аб'ектам*, гэта азначае, што [`this`](#function.this) унутры выкліканай функцыі +спасылаецца на глабальны аб'ект. + + function Foo() { + this.value = 42; + this.method = function() { + // this спасылаецца на глабальны аб'ект + console.log(this.value); // выведзе undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Заўвага:** Так як `setTimeout` прымае **функцыю** як першы параметр, +> распаўсюджанай з'яўляецца памылка выкарыстоўвання `setTimeout(foo(), 1000)`, у гэтым выпадку +> будзе выкарыстоўвацца **вернутае значэнне** выкліка `foo`, а **не** `foo`. Гэта, у большасці выпадкаў, +> маўклівая памылка, так як калі функцыя вяртае `undefined`, `setTimeout` +> **не** выклікае памылкі. + +### Паслядоўныя выклікі з дапамогай `setInterval` + +У той час калі `setTimeout` выконвае функцыю толькі адзін раз, `setInterval` - як бачна з назвы - +выконвае функцыю **кожныя** `X` milliseconds, але яе выкарыстанне не пажадана. + +Код, які выконваецца, блакуе выклік з таймаўтам, у той час `setInterval` будзе +планаваць выклікі зададзенай функцыі. Гэта можа, асабліва з маленькімі інтэрваламі, +прывесці да стварэння чаргі выклікаў функцый. + + function foo(){ + // нешта, што блакуе на 1 секунду + } + setInterval(foo, 100); + +У прыведзеным кодзе `foo` будзе выклікана аднойчы і заблакуе выкананне на адну секунду. + +У той час калі `foo` блакуе код, `setInterval` будзе планаваць наступныя яе выклікі +А калі выкананне `foo` скончана, ужо **дзесяць** наступных выклікаў будуць чакаць выканання. + +### Праца з магчыма блакуючым кодам + +Найбольш простае і кіруемае рашэнне гэта выкарыстоўваць `setTimeout` унутры самой функцыі. + + function foo(){ + // нешта, што блакуе на 1 секунду + setTimeout(foo, 100); + } + foo(); + +Гэты падыход не толькі інкапсулюе выклік `setTimeout`, але таксама прадухіляе +стварэнне чаргі выклікаў і дае дадатковы кантроль. Цяпер `foo` можа сама вырашыць +хоча яна выконвацца яшчэ раз ці не. + +### Ручная чыстка таймаўтаў + +Чыстка таймаўтаў і інтэрвалаў здзяйсняецца перадачай адпаведнага ID у +`clearTimeout` або `clearInterval`, гледзячы якая `set` функцыя была выкарыстана +да гэтага. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Чыстка ўсіх таймаўтаў + +Так як няма ўбудаванага метада для выдалення ўсіх таймаўтаў і/або інтэрвалаў, +для гэтага неабходна выкарыстоўваць брутфорс. + + // выдаліць "усе" таймаўты + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Але могуць быць таймаўты, якія не закрануты гэтым адвольным нумарам. +Іншы шлях ажыццяўлення гэтага - прыняць, што ID таймаўта павялічваецца на +адзін пасля кожнага выкліку `setTimeout`. + + // выдаліць "усе" таймаўты + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Не гледзячы на тое, што зараз гэта працуе на ўсіх асноўных браўзерах, тое, што ID +павінны быць арганізаваны такім шляхам не пазначана ў спецыфікацыі і можа змяніцца. Таму +замест гэтага рэкамендуецца сачыць за ўсімі ID таймаўтаў, каб яны маглі быць выдалены паасобку. + +### Схаванае выкарыстанне `eval` + +`setTimeout` і `setInterval` таксама могуць прымаць радок у якасці першага параметра. +Гэту магчымасць **ніколі** не трэба выкарыстоўваць, бо ўнутрана вызываецца `eval`. + +> **Заўвага:** Дакладны механізм работы пры перадачы радка можа адрозніваецца ў +> розных JavaScript рэалізацыях. Напрыклад, JScript ад Microsoft выкарыстоўвае +> канструктар `Function` замест `eval`. + + function foo() { + // будзе выклікана + } + + function bar() { + function foo() { + // ніколі не будзе выклікана + } + setTimeout('foo()', 1000); + } + bar(); + +Паколькі `eval` не выклікана [напрамую](#core.eval), радок, які перададзены +`setTimeout` будзе выкананы ў *глабальным скоўпе*; такім чынам, не будзе выкарыстана +лакальная пераменная `foo` са скоўпа `bar`. + +Адсюль вынікае рэкамендацыя **не** выкарыстоўваць радок для перадачы аргументаў у +функцыю, якая будзе вызывацца адной з таймаўт функцый. + + function foo(a, b, c) {} + + // НІКОЛІ так не рабіце + setTimeout('foo(1, 2, 3)', 1000) + + // Замест выкарыстоўвайце ананімныя функцыі + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Заўвага:** Нягледзячы на тое, што сінтаксіс `setTimeout(foo, 1000, 1, 2, 3)` можна выкарыстоўваць, +> гэта не рэкамендуецца, так як можа прывесці да +> непрыкметных памылак пры выкарыстанні з [метадамі](#function.this). +> Больш таго, сінтаксіс можа не працаваць у некаторых рэалізацыях JavaScript. +> Напрыклад, Internet Explorer ад Microsoft [**не** перадае аргументы напрамую зваротнаму выкліку](3). + +### У заключэнне + +Радок **ніколі** не павінен быць выкарыстаны як параметр `setTimeout` ці +`setInterval`. Відавочны знак **сапраўды** благога кода гэта калі функцыя, якая будзе выклікана, +патрабуе аргументы. Трэба перадаваць *ананімную функцыю*, якая будзе адказваць за +выклік патрэбнай функцыі. + +Больш таго, трэба пазбягаць выкарыстання `setInterval`, таму што яе планавальнік не блакуецца +выкананнем JavaScript. + +[1]: http://www.nczonline.net/blog/2009/09/29/web-definitions-dom-ajax-and-more/ "Web definitions: DOM, Ajax, and more" +[2]: http://www.w3.org/TR/2014/WD-html5-20140617/webappapis.html#timers "6 Web application APIs - HTML5" +[3]: http://msdn.microsoft.com/en-us/library/ie/ms536753(v=vs.85).aspx "setTimeout method (Internet Explorer)" diff --git a/external/JavaScript-Garden/doc/by/types/casting.md b/external/JavaScript-Garden/doc/by/types/casting.md new file mode 100644 index 0000000..763bf3a --- /dev/null +++ b/external/JavaScript-Garden/doc/by/types/casting.md @@ -0,0 +1,71 @@ +## Прывядзенне тыпаў + +JavaScript - *слаба тыпізаваная* мова, таму *прывядзенне тыпаў* адбываецца +**паўсюль** дзе магчыма. + + // Гэтыя равенствы - праўдзівыя + new Number(10) == 10; // аб'ект тыпа Number пераўтвараецца у + // лікавы прымітыў, праз няяўны выклік + // метада Number.prototype.valueOf + + 10 == '10'; // Strings пераўтвараецца ў Number + 10 == '+10 '; // Троху вар'яцтва з радкамі + 10 == '010'; // і яшчэ + isNaN(null) == false; // null пераўтвараецца ў 0 + // які вядома ж не NaN + + // Гэтыя равенствы - ілжывыя + 10 == 010; + 10 == '-10'; + +> **Заўвага для ES5:** Лічбавыя літэралы, што пачынаюцца з `0` інтэрпрэтуюцца як +> васьмірычныя (Base 8). Падтрымка васьмірычнай сістэмы была **прыбраная** у +> ECMAScript 5 strict mode. + +Каб пазбегнуць вышэйпрыведзеных праблемаў, **настойліва** ракамендуецца выкарыстоўваць +[аператар строгай роўнасці](#types.equality). Зрэшты, хоць гэта і пазбаўляе ад +многіх распаўсюджаных праблемаў, існуе яшчэ шмат праблемаў, які ўзнікаюць праз +слабую тыпізацыю JavaScript. + +### Канструктары ўбудаваных тыпаў + +Канструктары ўбудаваных тыпаў, напрыклад, `Number` і `String` паводзяць сябе +па рознаму, у залежнасці ад таго, выклікаюцца яны з ключавым словам `new` або без яго. + + new Number(10) === 10; // False, Object і Number + Number(10) === 10; // True, Number і Number + new Number(10) + 0 === 10; // True, праз неяўнае прывядзенне + +Выкарыстанне ўбудаванага тыпу, такога як `Number` у якасці канструкта створыць новы +экзэмпляр аб'екта `Number`, але пры адсутнасці ключавога слова `new` функцыя +`Number` будзе паводзіць сябе як канвертар. + +У дадатак, выкарытоўванне літэралаў, або значэнняў якія не з'яўляюцца аб'ектамі +прывядзе да дадатковых прывядзенняў тыпаў. + +Лепшы варыянт - гэта **яўнае** прывядзенне да аднаго з трох магчымых тыпаў. + +### Прывядзенне да радка + + '' + 10 === '10'; // true + +Праз даданне да значэння пустога радка, яно лёгка прыводзіцца да радка. + +### Прывядзенне да лікавага тыпу + + +'10' === 10; // true + +Выкарыстоўваючы **унарны** аператар плюс, магчыма пераўтварыць значэнне ў нумар. + +### Прывядзенне да булевага тыпу + +Выкарыстоўваючы аператар **адмаўленне (!)** двойчы, значэнне можна прыведзена +да лагічнага (булевага) тыпу. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true diff --git a/external/JavaScript-Garden/doc/by/types/equality.md b/external/JavaScript-Garden/doc/by/types/equality.md new file mode 100644 index 0000000..0859ce0 --- /dev/null +++ b/external/JavaScript-Garden/doc/by/types/equality.md @@ -0,0 +1,70 @@ +## Роўнасць і параўнанне + +У JavaScript роўнасць значэнняў аб'ектаў можна вызначыць двумя спосабамі. + +### Аператар роўнасці + +Аператар роўнасці складаецца з двух сімвалаў 'роўна': `==` + +JavaScript мае *слабую тыпізацыю*. Гэта значыць што аператар роўнасці +**прыводзіць** тыпы аб'ектаў, каб параўнаць іх. + + "" == "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 + +Вышэй прыведзеная табліца паказвае вынікі прывядзення тыпаў, і гэта галоўная прычына +па якой выкаростоўванне `==` лічыцца дрэннай практыкай. Яно прыводзіць да памылак +якія цяжка адсачыць праз складаны механізм прывядзення тыпаў. + +Акрамя гэтага, прывядзенне тыпаў таксама ўплывае на вытворчасць; +напрыклад, радок мае быць ператвораны ў нумар, перад тым як быць параўнаным з +іншым нумарам. + +### Аператар строгай роўнасці + +Аператар строгай роўнасці складаецца з **трох** сімвалаў 'роўна': `===`. + +Ён дзейнічае як звычайны аператар роўнасці, за выключэннем таго, што строгая +роўнасць **не** прыводзіць аперанды да агульнага тыпу. + + "" === "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 + +Вышэй прыведзеныя вынікі значна больш зразумелыя і даюць магчымасць хутчэй выявіць +памылкі ў кодзе. Гэта паляпшае код, а таксама дае прырост вытворчасці, у выпадку +калі аперанды розных тыпаў. + +### Параўнанне аб'ектаў + +Хоць абодва аператар `==` і `===` называюцца аператарамі **роўнасці**, яны паводзяць +сабе па рознаму калі хоць адзін аперанд тыпа `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Тут абодва аператанда параўноўваюцца на **ідэнтычнасць**, а **не** на роўнасць; +то бок будзе праверана, ці з'яўляюцца яны адным **экзэмплярам** аб'екта. Гэтак жа, +як `is` у Python, або параўнанне ўказальнікаў у C. + +### У заключэнне + +Настойліва рэкамендуецца выкарыстоўваць толькі аператар **строгай роўнасці**. +У выпадку, калі тыпы маюць быць прыведзеныя, гэта варта рабіць [яўна](#types.casting), +а не пакідаць іх на сумленні складаных правілаў прывядзення мовы праграмавання. diff --git a/external/JavaScript-Garden/doc/by/types/instanceof.md b/external/JavaScript-Garden/doc/by/types/instanceof.md new file mode 100644 index 0000000..220bc3c --- /dev/null +++ b/external/JavaScript-Garden/doc/by/types/instanceof.md @@ -0,0 +1,37 @@ +## Аператар `instanceof` + +Аператар `instanceof` параўноўвае канструктары двух аперандаў. Гэта карысна толькі +для параўнання аб'ектаў не ўбудаваных тыпаў. Выкарыстоўванне на ўбудаваных тыпах не +мае сэнсу, як і [аператар typeof](#types.typeof). + +### Параўнанне адвольных аб'ектаў + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Калі толькі прысвоім Bar.prototype аб'ект функцыі Foo, + // але не самаго экзэмпляра Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Выкарыстоўванне `instanceof` з убудаванымі тыпамі + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +Варта адзначыць, што `instanceof` не працуе на аб'ектах, якія паходзяць з розных +кантэкстаў JavaScript (напрыклад, розных дакументаў у web-браузеры), бо іх канструктары +насамрэч не будуць канструктарамі тых самых аб'ектаў. + +### У заключэнне + +Аператар `instanceof` мае быць выкарыстаны **толькі** для працы з аб'ектамі не +ўбудаваных тыпаў якія паходзяць з аднаго кантэкста JavaScript. Як і ў выпадку з +[аператарам `typeof`](#types.typeof), трэба **пазбягаць** любога іншага яго выкарыстання. diff --git a/external/JavaScript-Garden/doc/by/types/typeof.md b/external/JavaScript-Garden/doc/by/types/typeof.md new file mode 100644 index 0000000..7fb16ba --- /dev/null +++ b/external/JavaScript-Garden/doc/by/types/typeof.md @@ -0,0 +1,80 @@ +## Аператар `typeof` + +Аператар `typeof` (разам з [`instanceof`](#types.instanceof)) магчыма найбольшая +хіба мовы JavaScript, таму што ён амаль што **цалкам зламаны**. + +Хаця `instanceof` усё яшчэ мае абмежаванае ўжыванне, `typeof` можа быць выкарыстаны +толькі з адной мэтай, і гэта дарэчы **не** праверка тыпа. + +> **Заўвага:** Хаця для выкліка `typeof` можна выкарыстаць сінтаксіс функцыі, то бок +> `typeof(obj)`, гэта не выклік функцыі. Дужкі будуць апрацоўвацца нармальна +> і вернутае значэнне будзе выкарыстана як аперанд аператара `typeof`. +> **Не існуе** функцыі `typeof`. + +### Табліца тыпаў JavaScript + + Значэнне Клас Тып + ------------------------------------- + "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 + +У вышэй прыведзенай табыліцы, *Тып* паказвае значэнне вернутае аператарам `typeof`. +Як можна пабачыць, гэта значэнне абсалютна не кансістэнтнае. + +*Клас* паказвае значэнне ўнутраннай уласцівасці `[[Class]]` аб'екта. + +> **З спецыфікацыі:** значэнне `[[Class]]` можа быць быць адным з наступных +> радкоў. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +### Клас аб'екта + +Адзіны спосаб атрымаць значэнне `[[Class]]` аб'екта - выклікаць метад `Object.prototype.toString`. +Ён верне радок у наступным фармаце: `'[object ' + valueOfClass + ']'`, напрыклад +`[object String]` або `[object Array]`: + + 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 + +У вышэйпрыведзеным прыкладзе, `Object.prototype.toString` выклікаецца са значэннем +[this](#function.this) пазначаным як аб'ект чыё значэнне `[[Class]]` мае быць +атрыманым. + +> **Заўвага для ES5:** у ECMAScript 5, для зручнасці, значэнне `Object.prototype.toString` +> для `null` і `undefined` было **зменена** з `Object` на `Null` і +> `Undefined` адпаведна. + +### Праверка вызначанасці пераменных + + typeof foo !== 'undefined' + +Вышэйпрыведзены код праверыць ці было вызначана `foo`; просты зварот да пераменнай +прывядзе да `ReferenceError`. Гэта адзінае для чаго карысны `typeof`. + +### У заключэнне + +Каб праверыць тып аб'екта, настойліва рэкамендуецца выкарыстоўваць +`Object.prototype.toString` - гэта адзіны надзейны спосаб. +Як паказана ў вышэйпрыведзенай табліцы, некаторыя значэнні вернутыя аператарам +`typeof` не вызначаныя ў спецыфікацыі; такім чынам, яны могуць быць рознымі ў +розных рэалізацыях. + +Акрамя як для праверкі вызначанасці пераменнай, `typeof` мае быць пазбегнуты. diff --git a/external/JavaScript-Garden/doc/de/index.json b/external/JavaScript-Garden/doc/de/index.json new file mode 100644 index 0000000..63bb6ea --- /dev/null +++ b/external/JavaScript-Garden/doc/de/index.json @@ -0,0 +1,16 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in Deutsch", + "description": "Ein Guide über JavaScript's Ecken und Kanten.", + "sections": [ + { + "title": "Einführung", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "license" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/de/intro/authors.md b/external/JavaScript-Garden/doc/de/intro/authors.md new file mode 100644 index 0000000..f88f94a --- /dev/null +++ b/external/JavaScript-Garden/doc/de/intro/authors.md @@ -0,0 +1,8 @@ +## The Authors + +This guide is the work of two lovely Stack Overflow users, [Ivo Wetzel][1] +(Writing) and [Zhang Yi Jiang][2] (Design). + +[1]: http://stackoverflow.com/users/170224/ivo-wetzel +[2]: http://stackoverflow.com/users/313758/yi-jiang + diff --git a/external/JavaScript-Garden/doc/de/intro/contributors.md b/external/JavaScript-Garden/doc/de/intro/contributors.md new file mode 100644 index 0000000..73fb98e --- /dev/null +++ b/external/JavaScript-Garden/doc/de/intro/contributors.md @@ -0,0 +1,8 @@ +## Contributors + + - [Caio Romão][1] (Spelling corrections) + - [Andreas Blixt][2] (Language corrections) + +[1]: https://github.com/caio +[2]: https://github.com/blixt + diff --git a/external/JavaScript-Garden/doc/de/intro/index.md b/external/JavaScript-Garden/doc/de/intro/index.md new file mode 100644 index 0000000..01dca89 --- /dev/null +++ b/external/JavaScript-Garden/doc/de/intro/index.md @@ -0,0 +1,42 @@ +## Einführung + +**JavaScript Garden** ist eine wachsende Sammlung von Erklärungen der verzwicktesten Teile von JavaScript. Es gibt +Hinweise um häufige Fehler, Performance Probleme und schlechten Stil zu vermeiden. + +JavaScript Garden ist **keine** Anleitung um JavaScript zu lernen. Ein grundlegendes Verständnis der Sprache wird +wärmstens empfohlen. Eine gute [Einführung][1] findet sich zum Beispiel im Mozilla Developer Network. + +## Die Autoren + +Dieses Dokument wurde von zwei liebenswerten [Stack Overflow][2] Benutzern geschrieben: [Ivo Wetzel][3] +(Text) and [Zhang Yi Jiang][4] (Design). + +## Beitragende + + - [Beitragende](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## Hosting + +JavaScript Garden wird von GitHub bereitgestellt, aber es wird auch von [Cramer Development][7] unterstützt durch +einen Mirror auf [JavaScriptGarden.info][8]. + +## Lizenz + +JavaScript Garden wurde unter der [MIT Lizenz][9] veröffentlich und wird von [GitHub][10] veröffentlicht. Wenn du +Fehler findest mach bitte ein [Ticket][11] auf oder einen pull request ins repository. Du kannst uns auch im +[JavaScript Raum][12] des Stack Overflow Chats finden. + +Mehr demnächst. + +[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/external/JavaScript-Garden/doc/de/intro/license.md b/external/JavaScript-Garden/doc/de/intro/license.md new file mode 100644 index 0000000..cd39869 --- /dev/null +++ b/external/JavaScript-Garden/doc/de/intro/license.md @@ -0,0 +1,12 @@ +## License + +JavaScript Garden is published under the [MIT license][1] and hosted on +[GitHub][2]. If you find errors or typos please [file an issue][3] or a pull +request on the repository. You can also find us in the [JavaScript room][4] on +Stack Overflow chat. + +[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 + diff --git a/external/JavaScript-Garden/doc/en/array/constructor.md b/external/JavaScript-Garden/doc/en/array/constructor.md new file mode 100644 index 0000000..408ec55 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/array/constructor.md @@ -0,0 +1,33 @@ +## The `Array` Constructor + +Since the `Array` constructor is ambiguous in how it deals with its parameters, +it is highly recommended to use the array literal - `[]` 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 when 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 + +Being able to set the length of the array in advance is only useful in a few +cases, like repeating a string, in which it avoids the use of a loop. + + new Array(count + 1).join(stringToRepeat); + +### In Conclusion + +Literals are preferred to the Array constructor. They are shorter, have a clearer syntax, and increase code +readability. + diff --git a/external/JavaScript-Garden/doc/en/array/general.md b/external/JavaScript-Garden/doc/en/array/general.md new file mode 100644 index 0000000..a78091d --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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`](#object.forinloop) loop. 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**. + +Because the `for in` loop enumerates all the properties that are on the prototype +chain and because 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, which 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 arr = [1, 2, 3, 4, 5, 6]; + arr.length = 3; + arr; // [1, 2, 3] + + arr.length = 6; + arr.push(4); + arr; // [1, 2, 3, undefined, undefined, undefined, 4] + +Assigning a smaller length truncates the array. Increasing it creates a sparse 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/external/JavaScript-Garden/doc/en/core/delete.md b/external/JavaScript-Garden/doc/en/core/delete.md new file mode 100644 index 0000000..8e71241 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/core/delete.md @@ -0,0 +1,85 @@ +## 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 the Activation object or +the Global object. Such properties have a set of attributes, one of which is +`DontDelete`. Variable and function declarations in global and function code +always create properties with `DontDelete`, and 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 + +Explicitly set properties can be deleted normally. + + // 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` attribute. That's why the 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 its property +which allows us to delete it. + +IE (at least 6-8) has some bugs, so the code above doesn't work. + +### Function arguments and built-ins + +Functions' normal arguments, [`arguments` objects](#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 + +The behaviour of `delete` operator can be unpredictable for hosted objects. Due +to the specification, host objects are allowed to implement any kind of behavior. + +### In conclusion + +The `delete` operator often has unexpected behaviour and can only be safely +used to delete explicitly set properties on normal objects. diff --git a/external/JavaScript-Garden/doc/en/core/eval.md b/external/JavaScript-Garden/doc/en/core/eval.md new file mode 100644 index 0000000..6fa000f --- /dev/null +++ b/external/JavaScript-Garden/doc/en/core/eval.md @@ -0,0 +1,47 @@ +## Why Not to Use `eval` + +The `eval` function will execute a string of JavaScript code in the local scope. + + var number = 1; + function test() { + var number = 2; + eval('number = 3'); + return number; + } + test(); // 3 + number; // 1 + +However, `eval` only executes in the local scope when it is being called +directly *and* when the name of the called function is actually `eval`. + + var number = 1; + function test() { + var number = 2; + var copyOfEval = eval; + copyOfEval('number = 3'); + return number; + } + test(); // 2 + number; // 3 + +The use of `eval` should be avoided. 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, because 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 should be questioned +in its workings, performance and security. If something requires `eval` in +order to work, it 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/external/JavaScript-Garden/doc/en/core/semicolon.md b/external/JavaScript-Garden/doc/en/core/semicolon.md new file mode 100644 index 0000000..e6b2563 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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, so it is possible to omit them. + +JavaScript is not a semicolon-less language. In fact, it 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 because 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 +> that are followed by a new line. While this is not necessarily 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 recommended +that braces be kept on the same line as their corresponding statements and to +never omit them for single-line `if` / `else` statements. These measures will +not only improve the consistency of the code, but they will also prevent the +JavaScript parser from changing code behavior. + diff --git a/external/JavaScript-Garden/doc/en/core/undefined.md b/external/JavaScript-Garden/doc/en/core/undefined.md new file mode 100644 index 0000000..1bea541 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/core/undefined.md @@ -0,0 +1,74 @@ +## `undefined` and `null` + +JavaScript has two distinct values for nothing, `null` and `undefined`, with +the latter being more useful. + +### 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`. However, this variable is **neither** a constant +nor 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`. + +Here are some examples of when the value `undefined` is returned: + + - Accessing the (unmodified) global variable `undefined`. + - Accessing a declared *but not* yet initialized variable. + - Implicit returns of functions due to missing `return` statements. + - `return` statements that do not explicitly return anything. + - Lookups of non-existent properties. + - Function parameters that do not have any explicit value passed. + - Anything that has been set to the value of `undefined`. + - Any expression in the form of `void(expression)` + +### 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. + +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 `undefined` + + })('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 here is 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/external/JavaScript-Garden/doc/en/function/arguments.md b/external/JavaScript-Garden/doc/en/function/arguments.md new file mode 100644 index 0000000..fec3d8e --- /dev/null +++ b/external/JavaScript-Garden/doc/en/function/arguments.md @@ -0,0 +1,132 @@ +## 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); + +Because 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 turn methods - functions that use the +value of `this` as well as their arguments - into normal functions which only use their arguments. + + function Person(first, last) { + this.first = first; + this.last = last; + } + + Person.prototype.fullname = function(joiner, options) { + options = options || { order: "western" }; + var first = options.order === "western" ? this.first : this.last; + var last = options.order === "western" ? this.last : this.first; + return first + (joiner || " ") + last; + }; + + // Create an unbound version of "fullname", usable on any object with 'first' + // and 'last' properties passed as the first argument. This wrapper will + // not need to change if fullname changes in number or order of arguments. + Person.fullname = function() { + // Result: Person.prototype.fullname.call(this, joiner, ..., argN); + return Function.call.apply(Person.prototype.fullname, arguments); + }; + + var grace = new Person("Grace", "Hopper"); + + // 'Grace Hopper' + grace.fullname(); + + // 'Turing, Alan' + Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" }); + + +### 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 only time the `arguments` object is not created is 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, but it also breaks encapsulation +because the function may now be dependent on a specific calling context. + +Making use of `arguments.callee` or any of its properties is **highly discouraged**. + +> **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/external/JavaScript-Garden/doc/en/function/closures.md b/external/JavaScript-Garden/doc/en/function/closures.md new file mode 100644 index 0000000..aef633b --- /dev/null +++ b/external/JavaScript-Garden/doc/en/function/closures.md @@ -0,0 +1,116 @@ +## Closures and References + +One of JavaScript's most powerful features is the availability of *closures*. +With closures, scopes **always** keep access to the outer scope, in which they +were defined. 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 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.hackFail = function() { + count = 1337; + }; + +The above code will **not** change the variable `count` in the scope of `Counter`, +since `foo.hackFail` 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 loop's 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`. At the time +`console.log` gets called, the `for loop` has already finished, and the value of +`i` has 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, which 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 other popular way to achieve this is to add an additional argument to +the `setTimeout` function, which passes these arguments to the callback. + + for(var i = 0; i < 10; i++) { + setTimeout(function(e) { + console.log(e); + }, 1000, i); + } + +Some legacy JS environments (Internet Explorer 9 & below) do not support this. + +There's yet another way to accomplish this by using `.bind`, which can bind +a `this` context and arguments to function. It behaves identically to the code +above + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } diff --git a/external/JavaScript-Garden/doc/en/function/constructors.md b/external/JavaScript-Garden/doc/en/function/constructors.md new file mode 100644 index 0000000..e84a6cf --- /dev/null +++ b/external/JavaScript-Garden/doc/en/function/constructors.md @@ -0,0 +1,127 @@ +## 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 Person(name) { + this.name = name; + } + + Person.prototype.logName = function() { + console.log(this.name); + }; + + var sean = new Person(); + +The above calls `Person` as constructor and sets the `prototype` of the newly +created object to `Person.prototype`. + +In case of an explicit `return` statement, the function returns the value +specified by that statement, but **only** if the return value is an `Object`. + + function Car() { + return 'ford'; + } + new Car(); // a new object, not 'ford' + + function Person() { + this.someValue = 2; + + return { + name: 'Charles' + }; + } + new Person(); // the returned object ({name:'Charles'}), not including someValue + +When the `new` keyword is omitted, the function will **not** return a new object. + + function Pirate() { + this.hasEyePatch = true; // gets set on the global object! + } + var somePirate = Pirate(); // somePirate is 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 Robot() { + var color = 'gray'; + return { + getColor: function() { + return color; + } + } + } + + new Robot(); + Robot(); + +Both calls to `Robot` return the same thing, a newly created object that +has a property called `getColor`, which is a +[Closure](#function.closures). + +It should also be noted that the call `new Robot()` does **not** affect the +prototype of the returned object. While the prototype will be set on the newly +created object, `Robot` 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 + +It is often recommended to **not** use `new` because forgetting its use may +lead to bugs. + +In order to create a new object, one should rather use a factory and construct a +new object inside of that factory. + + function CarFactory() { + var car = {}; + car.owner = 'nobody'; + + var milesPerGallon = 2; + + car.setOwner = function(newOwner) { + this.owner = newOwner; + } + + car.getMPG = function() { + return milesPerGallon; + } + + return car; + } + +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 + is contrary to 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 use it +**consistently**. + diff --git a/external/JavaScript-Garden/doc/en/function/general.md b/external/JavaScript-Garden/doc/en/function/general.md new file mode 100644 index 0000000..7e1c51c --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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 an 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*, 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 declared 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/external/JavaScript-Garden/doc/en/function/scopes.md b/external/JavaScript-Garden/doc/en/function/scopes.md new file mode 100644 index 0000000..33d525e --- /dev/null +++ b/external/JavaScript-Garden/doc/en/function/scopes.md @@ -0,0 +1,234 @@ +## 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 +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, which 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, +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 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 execution starts. JavaScript moves +the `var` statements, as well as `function` declarations, to the top of the +nearest surrounding scope. + + // var statements got moved here + var bar, someValue; // default to 'undefined' + + // the function declaration 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, although the `if` statement seemed to modify the *global +variable* `goo`, it actually modifies the *local variable* - after hoisting +has been applied. + +Without knowledge of *hoisting*, one might suspect the code below would raise a +`ReferenceError`. + + // check whether SomeImportantThing has been initialized + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +But of course, this works due to the fact that the `var` statement is being +moved to the top of the *global scope*. + + var SomeImportantThing; + + // other code might initialize 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 the function. + +For example, when trying to access a variable named `foo` inside the scope of a +function, JavaScript will look up 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 associated with having only one global namespace is the +likelihood 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 +be callable, they must first be evaluated. + + ( // evaluate the function inside the parentheses + function() {} + ) // and return the function object + () // call the result of the evaluation + +There are other ways to evaluate and directly call the function expression +which, while different in syntax, behave the same way. + + // A few other styles for directly invoking the + !function(){}() + +function(){}() + (function(){}()); + void function(){}(); + // and so on... + +### In Conclusion + +It is recommended to always use an *anonymous wrapper* to encapsulate code in +its own namespace. This does not only protect code against name clashes, but 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/external/JavaScript-Garden/doc/en/function/this.md b/external/JavaScript-Garden/doc/en/function/this.md new file mode 100644 index 0000000..83abff0 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/function/this.md @@ -0,0 +1,120 @@ +## How `this` Works + +JavaScript has a different concept of what the special name `this` refers to +than most other programming languages. 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, in 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 can be considered another +mis-design of the language because 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`, you can create a +local variable inside of `method` that refers to `Foo`. + + Foo.method = function() { + var self = this; + function test() { + // Use self instead of this here + } + test(); + }; + +`self` 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. + +As of ECMAScript 5 you can use the `bind` method combined with an anonymous function to achieve the same result. + + Foo.method = function() { + var test = function() { + // this now refers to Foo + }.bind(this); + test(); + }; + +### Assigning Methods + +Another thing that does **not** work in JavaScript is function aliasing, which 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, in +fact, it is 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 an instance of `Bar`, `this` will now refer to that +very instance. + + diff --git a/external/JavaScript-Garden/doc/en/index.json b/external/JavaScript-Garden/doc/en/index.json new file mode 100644 index 0000000..283e4b6 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in English", + "description": "A Guide to JavaScript's Quirks and Flaws.", + "sections": [ + { + "title": "Intro", + "dir": "intro", + "articles": ["index"] + }, + { + "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", + "delete" + ] + }, + { + "title": "Other", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/en/intro/index.md b/external/JavaScript-Garden/doc/en/intro/index.md new file mode 100644 index 0000000..411f8a7 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/intro/index.md @@ -0,0 +1,49 @@ +## 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 and 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). + +It's currently maintained by [Tim Ruffles](http://truffles.me.uk). + +## Contributors + +- Too many to list here, [see all contributors](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + + +## 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/external/JavaScript-Garden/doc/en/object/forinloop.md b/external/JavaScript-Garden/doc/en/object/forinloop.md new file mode 100644 index 0000000..28d586c --- /dev/null +++ b/external/JavaScript-Garden/doc/en/object/forinloop.md @@ -0,0 +1,66 @@ +## The `for in` Loop + +Just like the `in` operator, the `for in` loop 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. In +ECMAScript 3 and older, this is done using the [`hasOwnProperty`](#object.hasownproperty) +method of `Object.prototype`. + +Since ECMAScript 5, `Object.defineProperty` can be used with +`enumerable` set to `false` to add properties to objects (including `Object`) +without these properties being enumerated. In this case it is reasonable +to assume in application code that any enumerable properties have been added +for a reason and to omit `hasOwnProperty`, since it makes code more verbose and less +readable. In library code `hasOwnProperty` should still be used since +assumptions cannot be made about which enumerable properties might reside +on the prototype chain. + +> **Note:** Since `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 with older versions of ECMAScript. +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. + +In newer versions of ECMAScript, non-enumerable properties can be defined with +`Object.defineProperty`, reducing the risk of iterating over properties without +using `hasOwnProperty`. Nonetheless, care must be taken when using older +libraries like [Prototype][1], which does not yet take advantage of new ECMAScript features. +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` in ECMAScript 3 or lower, as well as +in library code. Assumptions should never be made in these environments about whether +the native prototypes have been extended or not. Since ECMAScript 5, `Object.defineProperty` +makes it possible to define non-enumerable properties and to omit `hasOwnProperty` in +application code. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/en/object/general.md b/external/JavaScript-Garden/doc/en/object/general.md new file mode 100644 index 0000000..8958922 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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 sayHello(){} + sayHello.count = 1; + sayHello.count; // 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 that can be used to make number literals act +as objects too. + + 2..toString(); // the second point is correctly recognized + 2 .toString(); // note the space left of the dot + (2).toString(); // 2 is evaluated first + +### Objects as a Data Type + +Objects in JavaScript can also be used as [*Hashmaps*][1]; they mainly consist +of named properties mapping to values. + +Using an object literal - `{}` notation - it is possible to create a +plain object. This new object [inherits](#object.prototype) from `Object.prototype` and +does not have [own properties](#object.hasownproperty) defined. + + var names = {}; // a new empty object + + // a new object with a 'name' property with value 'Rob' + var rob = {name: 'Rob'}; + +### Accessing Properties + +The properties of an object can be accessed in two ways, via either the dot +notation or the square bracket notation. + + var pet = {name: 'kitten'} + pet.name; // kitten + pet['name']; // kitten + + var get = 'name'; + pet[get]; // kitten + + pet.1234; // SyntaxError + pet['1234']; // works + +The notations work almost identically, with the only difference being that the +square bracket notation allows for dynamic setting of properties and +the use of property names that would otherwise lead to a syntax error. + +### Deleting Properties + +The only way to remove a property from an object is to use the `delete` +operator; setting the property to `undefined` or `null` only removes the +*value* associated with the property, but not the *key*. + + var obj = { + a: 1, + b: 2, + c: 3 + }; + obj.a = undefined; + obj.b = null; + delete obj.c; + + for(var i in obj) { + if (obj.hasOwnProperty(i)) { + console.log(i, '' + obj[i]); + } + } + +The above outputs both `a undefined` and `b null` - only `c` 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, so me too' // 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/external/JavaScript-Garden/doc/en/object/hasownproperty.md b/external/JavaScript-Garden/doc/en/object/hasownproperty.md new file mode 100644 index 0000000..10423b0 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/object/hasownproperty.md @@ -0,0 +1,58 @@ +## `hasOwnProperty` + +To check whether an 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. See the section +on [`for in` loops](#object.forinloop) for more details on when to use +`hasOwnProperty` when iterating over object +properties. + +### `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` 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 + + // It's also possible to use hasOwnProperty from the Object + // prototype for this purpose + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + + +### In Conclusion + +Using `hasOwnProperty` is the **only** reliable method to check for the +existence of a property on an object. It is recommended that `hasOwnProperty` +be used in many cases when iterating over object properties as described +in the section on [`for in` loops](#object.forinloop). + diff --git a/external/JavaScript-Garden/doc/en/object/prototype.md b/external/JavaScript-Garden/doc/en/object/prototype.md new file mode 100644 index 0000000..f63abaa --- /dev/null +++ b/external/JavaScript-Garden/doc/en/object/prototype.md @@ -0,0 +1,118 @@ +## 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 a +prototypal model, while the other way around is a far more difficult task. + +JavaScript is the only widely used language that features prototypal +inheritance, so it can take time to adjust to the differences between the two +models. + +The first major difference is that inheritance in JavaScript uses *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', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +In the code 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. + +If 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. However, +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, and this may be significant in code where +performance is critical. 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 be 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 popular 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 **essential** to understand the prototypal inheritance model before +writing complex code that makes use of it. Also, be aware of the length of the +prototype chains in your code and break them up if necessary to avoid possible +performance problems. 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/external/JavaScript-Garden/doc/en/other/timeouts.md b/external/JavaScript-Garden/doc/en/other/timeouts.md new file mode 100644 index 0000000..e27ccac --- /dev/null +++ b/external/JavaScript-Garden/doc/en/other/timeouts.md @@ -0,0 +1,172 @@ +### `setTimeout` and `setInterval` + +Since JavaScript is asynchronous, it is possible to schedule the execution of a +function using the `setTimeout` and `setInterval` functions. + +> **Note:** Timeouts are **not** part of the ECMAScript standard. They were +> implemented in [BOM, or DOM Level 0][1], which are never defined nor +> documented formally. No recommended specification has been published so far, +> however, they are currently being standardized by [HTML5][2]. Due to this +> nature, the implementation may vary from browsers and engines. + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +When `setTimeout` is called, it returns the ID of the timeout and schedule +`foo` to run **approximately** one thousand milliseconds in the future. +`foo` will then be executed **once**. + +Depending on the timer resolution of the JavaScript engine 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 specified in the `setTimeout` call. + +The function that was passed as the first parameter will get called by the +*global object*, which means that [`this`](#function.this) inside the called function +refers to the global 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, a +> common 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, 1000); + +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 solution, as well as most controllable solution, is to use `setTimeout` within +the function itself. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 1000); + } + foo(); + +Not only does this encapsulate the `setTimeout` call, but it also prevents the +stacking of calls and 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 on 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); + } + +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 every time you call `setTimeout`. + + // clear "all" timeouts + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Even though this works on all major browsers today, 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` + +`setTimeout` and `setInterval` can also take a string as their first parameter. +This feature should **never** be used because it internally makes use of `eval`. + +> **Note:** The exact workings when a string is passed to them might differ in +> various JavaScript implementations. For example, Microsoft's JScript uses +> 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 be 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 to pass 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(1, 2, 3); + }, 1000) + +> **Note:** While it is also possible to use `setTimeout(foo, 1000, 1, 2, 3)` +> syntax, it is not recommended, as its use may lead +> to subtle errors when used with [methods](#function.this). +> Furthermore, the syntax might not work in some JavaScript implementations. +> For example, Microsoft's Internet Explorer [does **not** pass the arguments directly to the callback](3). + +### In Conclusion + +A string should **never** 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. + +Furthermore, the use of `setInterval` should be avoided because its scheduler is not +blocked by executing JavaScript. + +[1]: http://www.nczonline.net/blog/2009/09/29/web-definitions-dom-ajax-and-more/ "Web definitions: DOM, Ajax, and more" +[2]: http://www.w3.org/TR/2014/WD-html5-20140617/webappapis.html#timers "6 Web application APIs - HTML5" +[3]: http://msdn.microsoft.com/en-us/library/ie/ms536753(v=vs.85).aspx "setTimeout method (Internet Explorer)" diff --git a/external/JavaScript-Garden/doc/en/types/casting.md b/external/JavaScript-Garden/doc/en/types/casting.md new file mode 100644 index 0000000..f12aa81 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/types/casting.md @@ -0,0 +1,71 @@ +## Type Casting + +JavaScript is a *weakly typed* language, so it will apply *type coercion* +**wherever** possible. + + // These are true + new Number(10) == 10; // Number object is converted + // to a number primitive via implicit call of + // Number.prototype.valueOf method + + 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. + +To avoid the issues 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, passing literals or non-object values 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 an empty string, a value can easily be cast 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 to a boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/external/JavaScript-Garden/doc/en/types/equality.md b/external/JavaScript-Garden/doc/en/types/equality.md new file mode 100644 index 0000000..e47752a --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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 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 called **equality** operators, they behave +differently when at least one of their operands is 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/external/JavaScript-Garden/doc/en/types/instanceof.md b/external/JavaScript-Garden/doc/en/types/instanceof.md new file mode 100644 index 0000000..2fe4106 --- /dev/null +++ b/external/JavaScript-Garden/doc/en/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 originate 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 originate from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/external/JavaScript-Garden/doc/en/types/typeof.md b/external/JavaScript-Garden/doc/en/types/typeof.md new file mode 100644 index 0000000..9eb3fbd --- /dev/null +++ b/external/JavaScript-Garden/doc/en/types/typeof.md @@ -0,0 +1,84 @@ +## The `typeof` Operator + +The `typeof` operator (together with +[`instanceof`](#types.instanceof)) is probably the biggest +design flaw of JavaScript, as it is almost **completely broken**. + +Although `instanceof` still has 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 parentheses behave as 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`. + +### The Class of an Object + +The only way to determine an object's `[[Class]]` value is using `Object.prototype.toString`. It +returns a string in the following format: `'[object ' + valueOfClass + ']'`, e.g `[object String]` or +`[object Array]`: + + 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` because 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 between implementations. + +Unless checking whether a variable is defined, `typeof` should be avoided. + + diff --git a/external/JavaScript-Garden/doc/es/array/constructor.md b/external/JavaScript-Garden/doc/es/array/constructor.md new file mode 100644 index 0000000..46c5c16 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/array/constructor.md @@ -0,0 +1,35 @@ +## El constructor `Array` + +Desde el constructor `Array` es ambiguo en la forma en que ocupa sus párametros, +es recomendable siempre el uso de arrays literales - la notación `[]` - +cuando se crean nuevos arrays. + + [1, 2, 3]; // Resultado: [1, 2, 3] + new Array(1, 2, 3); // Resultado: [1, 2, 3] + + [3]; // Resultado: [3] + new Array(3); // Resultado: [] + new Array('3') // Resultado: ['3'] + +En casos cuando sólo hay un argumento pasado al constructor del `Array`, +y que el argumento es un `Número`, el contructor devolverá un array *disperso* +con la propiedad `length` establecida al valor del argumento. Esto debe señalarse +que la propiedad `length` **sólo** del nuevo array se establecerá de esa manera, +los índices reales de la matriz no se iniciará. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // falso, el índice no se ha establecido + +El comportamiento de poder establecer la longitud de un array inicial sólo es útil +en algunos casos array, como la repetición de una cadena, en la que se evita el uso +del código de `bucle for`. + + new Array(count + 1).join(stringToRepeat); + +### En conclusión + +El uso de un constructor `Array` debe ser devuelto como sea posible. +Los literales son definitivamente preferidos. Estos son más cortos y tienen una +sintaxis más limpia; por lo tanto, también se incrementa la legibilidad del código. + diff --git a/external/JavaScript-Garden/doc/es/array/general.md b/external/JavaScript-Garden/doc/es/array/general.md new file mode 100644 index 0000000..3de582b --- /dev/null +++ b/external/JavaScript-Garden/doc/es/array/general.md @@ -0,0 +1,58 @@ +## Iteración de un Array y sus propiedades + +A pesar que los arrays en JavaScript son objetos, no existe un buena razón para +usarlo en un [`bucle for`](#object.forinloop) para una interación de este. De +hecho, hay un número de buenas razones **contra** el uso de `for in` en arrays. + +> **Nota:** Los arrays de JavaScript **no** son *arrays asociativos*. JavaScript sólo +> tiene [objetos](#object.general) para el mapeo de keys a valores. Y mientras +> que los arrays asociativos **preservan** el orden, los objetos **no**. + +Dado que el bucle `for in` enumera todas las propiedades que están en una cadena +de prototipo y la única manera para excluir estas propiedades es el uso de +[`hasOwnProperty`](#object.hasownproperty), ya que es **veinte veces** más +lento que un bucle `for` normal. + +### Iteración + +Con el fin de obtener el mejor rendimiento cuando se repite la interación de arrays, +es lo mejor hacer uso del clásico bucle `for`. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Hay una captura adicional en el ejemplo anterior, que es el almacenamiento de la +caché de longitud del array vía `l = list.length`. + +Aunque la propiedad `length` es definida en el mismo array, todavía posee una sobrecarga +para realizar la búsqueda en cada interación del bucle. Y mientras que los últimos +motores de JavaScript **pueden** aplicar optimizaciones en este caso, no hay manera +de saber si el ćodigo se ejecutará en uno de estos nuevos motores nuevos o no. + +De hecho, dejando de lado el almacenamiento en caché puede resultar que el bucle +inicie sólo la **mitad de rápido** que con la longitud de la caché. + +### La propiedad `length` + +Mientras que *getter* de la propiedad `length` simplemente retorne el número de +elementos son contenidos en un array, el *setter* puede ser usado para +**truncar** el array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +La asignación de un menor número de longitud trunca al array, pero incrementando la +longitud no tiene ningún efecto sobre el array. + +### En conclusión + +Para obtener el mejor rendimiento es recomendable siempre usar el bucle `for` +y alamacenar en caché la propiedad `length`. El uso del bucle `for in` en un array +es señal de un código mal escrito propenso a errores y un mal desempeño. + diff --git a/external/JavaScript-Garden/doc/es/core/delete.md b/external/JavaScript-Garden/doc/es/core/delete.md new file mode 100644 index 0000000..1d0d198 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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. diff --git a/external/JavaScript-Garden/doc/es/core/eval.md b/external/JavaScript-Garden/doc/es/core/eval.md new file mode 100644 index 0000000..208bd28 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/core/eval.md @@ -0,0 +1,47 @@ +## ¿Por qué no usar `eval`? + +La función `eval` ejecuta un string como código JavaScript en el ámbito local. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Pero `eval` sólo ejecutará en ámbito local cuando es llamado **directamente** *y* +el nombre de la función llamada es `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +El uso de `eval` debe evitarse **a toda costa**. El 99.9% de su "uso" puede +lograrse **sin** su uso.. + +### `eval` disfrazado + +Las funciones de [tiempo de espera](#other.timeouts) `setTimeout` y `setInterval` pueden +tomar un string como primer argumento. En este caso, el string **siempre** se ejecutará en +el ámbito global ya que `eval` no ha sido llamado directamente. + +### Problemas de seguridad + +`eval` es también un problema de seguridad ya que ejecuta **cualquier** código enviado, +y **nunca** debe usarse con strings que no se conozcan o tengan un origen no confiable. + +### En conclusión + +`eval` nunca debe ser usado, cualquier código que haga uso del mismo debe ser cuestionado +en su funcionamiento, rendimiento y seguridad. En caso de que se necesite trabajar con +`eval`, el diseño ha de ser cuestionado y **no** debe utilizarse en primer lugar, se +debe usar un *mejor diseño*, que no requiera el uso de `eval`. + diff --git a/external/JavaScript-Garden/doc/es/core/semicolon.md b/external/JavaScript-Garden/doc/es/core/semicolon.md new file mode 100644 index 0000000..f03f7d8 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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, so it is possible to omit them. + +JavaScript is not a semicolon-less language. In fact, it 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 because 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, but they will also prevent the +JavaScript parser from changing its behavior. + diff --git a/external/JavaScript-Garden/doc/es/core/undefined.md b/external/JavaScript-Garden/doc/es/core/undefined.md new file mode 100644 index 0000000..41dde82 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/core/undefined.md @@ -0,0 +1,74 @@ +## `undefined` y `null` + +JavaScript tiene dos valores distintos para `nothing`, el más útil de estos dos +es `undefined`. + +### El valor `undefined` + +`undefined` es un tipo de dato con exactamente el mismo valor: `undefined`. + +El lenguaje también define una variable global que tiene el valor de `undefined`, +Esta variable es también llamada `undefined`. Sin embargo, esta variable **no** es una +constante, ni es una palabra reservada del lenguaje. Esto significa que el *valor* +puede ser sobreescrito fácilmente. + +> **Nota ES5:** `undefined` en ECMAScript 5 **ya no es** *modificable* en modo esstricto, +> pero su nombre todavía puede por ejemplo establecer una función con el nombre +> `undefined`. + +Algunos ejemplos cuando el valor retorna `undefined`: + + - Acceso a la variable global (sin modificar) `undefined`. + - Retorna implícitamente las funciones que no posean la sentencia `return`. + - Sentencia `return` que no retorna nada de forma explicíta. + - Búsquedas de propiedades inexistentes. + - Párametros de la función que no tienen ningún valor explicíto pasado. + - Cualquier valor que se estable en `undefined`. + +### Manejar los cambios en el valor deChanges `undefined` + +Dado que la variable `undefined` sólo tiene una copia del *value* de +`undefined`, assigna un nuevo valor que **no** cambie el valor del +*tipo* `undefined`. + + +Aún con el fin de comparar con el valor de `undefined` es necesario +recuperar el valor de `undefined` primero. + +Con el fin de proteger una posible sobreescritura en la variable `undefined`, +una técnica común es agregar un párametro adicional a un +[wrapper anónimo](#function.scopes), que consiga ningún párametro que se le pase. + + + var undefined = 123; + (function(something, foo, undefined) { + // undefined en el ámbito local + // ahora hace referencia al valor + + })('Hello World', 42); + +Otra forma de lograrlo un mismo efecto es declarar dentro un +wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +La única diferencia, es que está versión es de 4 bytes más que utiliza, y en +caso se comprima y no hay otra declaración de 'var' dentro del +wrapper anónimo. + + +### Uso de `null` + +Mientras que `undefined` en el contexto del lenguaje JavaScript es muy usado +en el sentido tradicional como *null*, el actual `null` (ambos literal y de un tipo) +es más o menos que otro tipo de datos. + +Es utilizado en algunos detalles internos de JavaScript (como declarar al final de un +cadena de prototipo estableciendo `Foo.prototype = null`), pero en casi todos los +casos, puede ser reemplazado por `undefined`. + diff --git a/external/JavaScript-Garden/doc/es/function/arguments.md b/external/JavaScript-Garden/doc/es/function/arguments.md new file mode 100644 index 0000000..0eff8da --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/arguments.md @@ -0,0 +1,119 @@ +## El objeto `arguments` + +Cada ámbito de la función de JavaScript puede acceder a la variable especial `arguments`. +Está variable contiene una lista de todos los argumentos que se pasan a la función. + +> **Nota:** En este caso `arguments` ya se ha definido dentro del ámbito de la +> función ya sea através de la sentencia `var` o como un parámetro formal, +> el objeto `arguments` no se creará. + +El objeto `arguments` **no** es un `Array`. Si bien cuenta con la semántica +de un array - concretamente la propiedad `length` - no hereda de +`Array.prototype` y es de hecho un `Objeto`. + +Debido a esto, **no** es posible usar los métodos estándar de los arrays como `push`, +`pop` o `slice` en `arguments`. Mientras que la iteración es un simple bucle `for` que +funciona muy bien, esto se convierte necesariamente en un `Array` real con el +fin de utilizar los métodos de un `Array`. + +### Conversión de un Array + +El siguiente código devuelve un nuevo `Array` que contiene todos los elementos del +objeto `arguments`. + + Array.prototype.slice.call(arguments); + +Esta conversión es **lenta**, **no es recomendable** usarlo en puntos criticos que +afecten el rendimiento del código. + +### Pasar Argumentos + +El siguiente método es recomendado para pasar argumentos desde una función a +otra. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Otro truco es utilizar tanto `call` y `apply` juntos para crear contenedores rápidos y +consolidados. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Crea una versión sin consolidar de "method" + // Se toma los parámetros: this, arg1, arg2...argN + Foo.method = function() { + + // Resultado: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Los parámetros formales y argumentos de índices + +El objeto `arguments` crea las funciones de *getter* y *setter* para sus +propiedades, así como parámetros formales de la función. + +Como resultado, se ha cambiado el valor formal del parámetro también se cambio el +valor de la propiedad correspondiente del objeto `arguments`, y al revés. + + 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); + +### Mitos y verdades sobre el rendimiento + +El objeto `arguments` es siempre creado con las dos únicas excepciones cuando es +el caso en que declarado como un nombre dentro de la función o uno de los +parámetros formales. No importa si se utiliza o no. + +Ambos *getters* y *setters* son **siempre** creados; por lo tanto, con que casi no se +tiene un impacto en el rendimiento en todo, especialemente no en el código real donde no +es más que un simple acceso a las propiedades del objeto `arguments`. + +> **Nota ES5:** Estos *getters* y *setters* no son creados en modo estricto. + +Sin embargo, hay casos en que se reducirá drásticamente el rendimiento en los motores +modernos de JavaScript. Este es el caso del uso de `arguments.callee`. + + function foo() { + arguments.callee; // realiza algo con la función del objeto + arguments.callee.caller; // y llama a la función del objeto + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Debería ser normalmente entre líneas... + } + } + +El código anterior, `foo` no puede estar sujeto a la [expansión en línea][1] ya que se +necesita saber acerca de sí mismo y la llamada. Esto no sólo denota los posibles beneficios +de rendimiento que surgen con la expansión en línea, ya que también interrumpe la encapsulación +ya que la función ahora puede ser dependiente de un contexto específico de llamada. + +Es **muy recomendable** **nunca** hacer uso de `arguments.callee` o de cualquier +de sus propiedades. + +> **Nota ES5:** En modo estricto, `arguments.callee` generará una excepción de `TypeError` ya que +> su uso ha quedado obsoleto. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/external/JavaScript-Garden/doc/es/function/closures.md b/external/JavaScript-Garden/doc/es/function/closures.md new file mode 100644 index 0000000..40a7721 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/closures.md @@ -0,0 +1,98 @@ +## Closures y referencias + +Una de las características más poderosas de JavaScript es la disponibilidad de *closures* (cerraduras), +esto significa que los ámbitos **siempre** podrán ser accedidos por ámbitos externos donde +fueron definidos. Dado que sólo el alcance es único en JavaScript en el +[ámbito de la función](#function.scopes), todas las funciones, por omisión, actúan como closures. + +### Emulando variables privadas + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +En este caso, `Counter` retorna **dos** closures. La función `increment` y la +función `get`. Ambas funciones mantienen el ámbito de la **referencia** de +`Counter` y, por lo tanto, siempre accede a la variable `count` que fue definido +en el ámbito. + +### ¿Por qué las variables privadas trabajan? + +Dado que no es posible referenciar o asignar ámbitos en JavaScript, **no** hay +manera de acceder a la variable `count` desde fuera. Sólo existe una forma para +interactuar con estos vía los dos closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +El código anterior **no** ha cambiado la variable `count` en el ámbito de `Counter`, +desde `foo.hack` no es definido en **ese** ámbito. En su lugar se creará - o +se anulará - la variable *global* `count`. + +### Closures dentro de bucles + +Un error frecuente en el uso de closures dentro de bucles, es como si se tratará +de copiar el valor del índice de la variable del bucle. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +El código anterior **no** tendrá como salida los números del `0` al `9`, sino +simplementemente se imprimirá el número `10` diez veces. + +La función *anónima* hace **referencia** a `i` y se llama a +`console.log`, el `bucle for` ya ha terminado y finalizo el valor de +`i` a `10`. + +Con el fin de obtener el comportamiento deseado, es necesario crear una **copia** +del valor de `i`. + +### Evitando el problema de referencia + +Con el fin de copiar el valor de la variable índice del bucle, lo mejor es utilizar +un [contenedor anónimo](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +La función anónima externa llamará inmediatamente a `i` como su primer +argumento y recibirá la copia del **valor** de `i` como parámetro de `e`. + +La función anónima que se pasa a `setTimeout` ahora es una referencia a +`e`, cuyo valor **no** han sido cambiados por el bucle. + +No hay otra manera de lograr esto; se debe retornar una función desde +el contenedor anónimo, que tendrá el mismo comportamiento que el código +anterior. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/external/JavaScript-Garden/doc/es/function/constructors.md b/external/JavaScript-Garden/doc/es/function/constructors.md new file mode 100644 index 0000000..9c4e294 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/constructors.md @@ -0,0 +1,129 @@ +## Constructores + +Los constructores en JavaScript todavía son diferentes a los de otros lenguajes. +Cualquier llamada que es precedida por la palabra `new` actua como un constructor. + +Dentro del constructor - la función llama - el valor de `this` se refiere a un +`Objeto` recién creado. El [`prototipo`](#object.prototype) de este **nuevo** +objeto se establece en el `prototipo` de la funcióno que es invocado como el +constructor. + +Si la función que se llama no tiene una sentencia `return` explícita, entonces +implícitamente devuelve el valor de `this` - el nuevo objeto. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +La llamada de `Foo` por encima del constructor y establece el `prototipo` del objeto +recién creado a `Foo.prototype`. + +En caso explícito de la sentencia `return` de la función devuelva el valor especificado +que la declaración, **pero sólo** si el valor devuelto es un `Object`. + + function Bar() { + return 2; + } + new Bar(); // a new object + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // the returned object + +Cuando una `nueva` keyword es omitidad, la función **no** devuelve un nuevo objeto. + + function Foo() { + this.bla = 1; // se establece en el objeto global + } + Foo(); // undefined + +Aunque el ejemplo anterior puede parecer que trabaja en algunos casos, debido +a los trabajos de [`this`](#function.this) en JavaScript, que usará el +*objeto global* como valor de `this`. + +### Fábricas + +Con el fin de ser capaz de omitir un `nuevo` keyword, la función del tiene +explícitamente devolver un valor. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Ambos llamadas a `Bar` devuelven exactamente lo mismo, un reciente objeto creado que +tiene como propiedad llamada el `method`, esto es un +[Closure](#function.closures). + +También hay que notar que la llamada `new Bar()` **no** afecta al prototipo +del objeto devuelto. Mientras que el prototipo se establece en el objeto recién creado, + `Bar` nunca devuelve un nuevo objeto. + +En el ejemplo anterior, no hay diferencia funcional entre usar y no usar +el keyword `new`. + + +### Creación de nuevos objetos vía Factorias + +Una recomendación a menudo es **no** utilizar `new` ya que su uso puede +conducir a errores. + +Con el fin de crear un nuevo objeto, uno bien debe utilizar una fábrica y un +constructor para crear un nuevo objeto dentro de la fábrica. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +Aunque lo anterior es robuesto frente a la keyword `new` y, ciertamente hace +que el uso de [variables privadas](#function.closures) sea fácil, esto viene con +algunas desventajas. + + 1. Se utiliza más memoria, ya que los objetos creados **no** comparten los métodos de + un prototipo. + 2. Con el fin de heredar de una fábrica se necesita copiar todos los métodos a otro + objeto o poner todo en un prototipo de nuevo objeto. + 3. La eliminación de una cadena de prototipo sólo por dejar la keyword `new` de + alguna manera va en contra del espíritu del lenguaje. + +### En conclusión + +Mientras que se omite el keyword `new` podría dar a errores, **no** es ciertamente +una razón para abandonar el uso de prototipos por completo. Al final todo se reduce a +la solución que se adapta mejor a las necesidades de la aplicación, especialmente si es +importante elegir un estilo específico en la creación de objetos +**y resistirse**. + + diff --git a/external/JavaScript-Garden/doc/es/function/general.md b/external/JavaScript-Garden/doc/es/function/general.md new file mode 100644 index 0000000..c051373 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/general.md @@ -0,0 +1,48 @@ +## La declaración de funciones y expresiones + +Las funciones en JavaScript son funciones de primera clase `(first class functions)`. +Esto significa que se pueden tratar como objetos. Un uso común de esta característica es pasar de +una *función anónima* a otra, posiblemente una función asíncrona. Esto se conoce como `callback`. + +### La declaración `function` + + function foo() {} + +La función anterior se [carga](#function.scopes) así mismo antes de iniciar la ejecución del +programa; por lo tanto, está disponible en *todo* el scope (ámbito) de la aplicación +donde se ha *definido*, aunque hubiera sido llamado antes de definirse en el código. + + foo(); // Funciona porque foo ha sido creado antes que este código se ejecute + function foo() {} + +### La expresión `function` + + var foo = function() {}; + +Este ejemplo asigna una función sin nombre y anónima a la variable `foo`. + + foo; // 'undefined' + foo(); // Lanza TypeError + var foo = function() {}; + +Debido a la declaración de `var`, que carga el nombre de la variable `foo` antes +de la ejecución real del inicio del código, `foo` ya estará definidido cuando se +ejecute el script. + +Pero se asigna sólo si ocurre en tiempo de ejecución, el valor de `foo` de forma +predetermina es [undefined](#core.undefined) antes de que el código se ejecute. + +### Expresión nombre de función + +Otro caso especial de asignación de nombre de funciones. + + var foo = function bar() { + bar(); // Funciona + } + bar(); // ReferenceError + +Aquí `bar` no está disponible en el ámbito externo (scope), ya que la función sólo es +asignada a `foo`; Sin embargo, dentro de `bar` si está disponible. Esto se debe a la forma +en como trabaja la [resolución de nombres](#function.scopes) en JavaScript, el nombre de +la función esta *siempre* disponible en el ámbito local de la propia función. + diff --git a/external/JavaScript-Garden/doc/es/function/scopes.md b/external/JavaScript-Garden/doc/es/function/scopes.md new file mode 100644 index 0000000..00ed940 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/scopes.md @@ -0,0 +1,231 @@ +## Ámbitos y Namespaces + +A pesar que JavaScript tiene una muy buena sintaxis de dos llaves para los bloques, +está **no** es compatible con el soporte de ámbito de bloques; por lo que todo se deja +al lenguaje con el *ámbito de la función*. + + function test() { // un ámbito + for(var i = 0; i < 10; i++) { // no es un ámbito + // cuenta + } + console.log(i); // 10 + } + +> **Nota:** Cuando no use una instrucción, de retorno o una función como +> argumento, la notación de `{...}` serán interpretadas como una declaración de bloques y +> **no** como un objeto literal. Esto, en conjunto con la +> [inserción automática de punto y coma](#core.semicolon), puede conducir a errores sutiles. + +Tampoco hay distintos namespaces en JavaScript, lo que significa que todo se define +en un namespace *global y compartido*. + +Cada vez que una variable es referenciada, JavaScript recorre hacia arriba a través de todos +los ámbitos hasta encontrarlo. En este caso que llegue al ámbito global y todavía no ha +encontrado el nombre solicitado, se generará un error `ReferenceError`. + +### El terror de las variables globales + + // script A + foo = '42'; + + // script B + var foo = '42' + +Estos dos scripts **no** tienen el mismo efecto. El script A define una variable +llamada `foo` en el ámbito *global* y el script B define `foo` en el +*actual* ámbito. + +Una vez más, esto **no** tiene el *mismo efecto* para todo, no usar `var` puede tener +mayor implicación. + + // ámbito global + var foo = 42; + function test() { + // ámbito local + foo = 21; + } + test(); + foo; // 21 + +Dejando de lado la sentencia `var` dentro de la función `test` sobre escribiría el +valor de `foo`. Si bien al principio puede parecer un gran cambio, se tiene +miles de líneas de código en JavaScript y no se usaría `var` introduciendose en un +horrible y difícil detección de errores. + + // ámbito global + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // ámbito de subLoop + for(i = 0; i < 10; i++) { // falta la sentencia var + // ¡realizar cosas asombrosas! + } + } + +El bucle externo terminará después de la primera llamada a `subLoop`, desde `subLoop` +sobreescribe el valor global de `i`. Usando `var` para el segundo bucle `for` se hace +fácil evitar este error. La sentencia `var` no debe **nunca** dejarse a menos que +el *efecto deseado* es afectado por el ámbito exterior. + +### Variables locales + +La única fuente para las variables locales en JavaScript son los parámetros de la +[función](#function.general) y variables que fueron declaradas vía la sentencia +`var`. + + // ámbito global + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // ámbito local de la función test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +Mientras `foo` y `i` son variables locales dentro del ámbitor de la función `test`, +ela asignación de `bar` sobreescribe la variable global con el mismo nombre. + +### Hoisting + +La declaración de **hoists** en JavaScript. Esto significa que tanto la declaración de `var` y +la `función` declarada se translada a la parte superior de su ámbito que lo contiene. + + 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]; + } + } + +El código anterior transforma antes de ejecutarse. JavaScript mueve +la declaracione `var` aspi como las declaraciones de la `función` a la parte superior a +lo más cercano del ámbito circundante. + + // declaraciones var movidas aquí + var bar, someValue; // por omisión 'undefined' + + // la función declarada es movida aquí también + function test(data) { + var goo, i, e; // se pierde el ámbito del bloque movido aquí + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // falla con TypeError desde bar sigue en 'undefined' + someValue = 42; // las asignaciones no se ven afectadas por hoisting + bar = function() {}; + + test(); + +La falta de alcance del bloque no sólo moverá la declaración `var` fuera de los bucles y +su contenido, sino también hará que los resultados de ciertos constructores `if` +no sean intuitivas. + +En el código original la declaración de `if` si parecía modificar la *variable +global* `goo`, mientras actualmente este modifica la *variable local* - después hoisting +ha sido aplicado. + +Sin el conocimiento acerca de *hoisting*, a continuación el código puede parecer +un `ReferenceError`. + + // comprueba si SomeImportantThing ha iniciado + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Pero, por supuesto, lo anterior funciona debido a que la declaración `var` es movida +a la parte superior del *ámbito global*. + + var SomeImportantThing; + + // otro código podría iniciar SomeImportantThing aqui, o no + + // asegúrese de que está ahí + 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, but 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/external/JavaScript-Garden/doc/es/function/this.md b/external/JavaScript-Garden/doc/es/function/this.md new file mode 100644 index 0000000..4fed1e3 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/function/this.md @@ -0,0 +1,111 @@ +## Cómo trabaja `this` + +JavaScript tiene un concepto diferente sobre el nombre especial `this` referido a la +mayoría de lenguajes de programación. Hay exactamente **cinco** formas distintas en donde +es posible ver el valor de `this` dentro de lo posible en el lenguaje. + +### El ámbito global (Global Scope) + + this; + +Cuando se utiliza `this` en el ámbito global, simplemente se refiere al objeto *global*. + + +### Llamar a una función + + foo(); + +Aquí `this` se refiere al objeto *global*. + +> **Nota ES5:** En modo estricto (strict mode), el objeto global **ya no** es accesible a través de this. +> `this` tendrá el valor de `undefined` en este caso. + +### Llamar a un método + + test.foo(); + +En este ejemplo `this` se referiere a `test`. + +### Llamar a un constructor + + new foo(); + +Llamar a una función que esta precedida por la palabra clave `new` actúa como +un [constructor](#function.constructors). Dentro de la función, `this` se refiere +al `Objeto` *recién creado*. + +### Ajuste explícito de `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // array que se apilará + foo.call(bar, 1, 2, 3); // resultados a = 1, b = 2, c = 3 + +Cuando se utiliza los métodos `call` o `apply` en `Function.prototype`, el valor de +`this` dentro de la función llamada se ajustará **explícitamente** al primer argumento +correspondiente a la llamada de la función. + +Como resultado, el ejemplo anterior sobre los *casos de métodos* estos **no** se aplican, y `this` +dentro de `foo` puede establecerse en `bar`. + +> **Nota:** `this` **no puede** ser usado para referirse a un objeto dentro de un `Objeto` +> literal. Así `var obj = {me: this}` **no** dará ninǵun resultado en `me` refiriendose a +> `obj`, ya que `this` sólo será obtenido por uno de los cincos casos enumerados. + +### Errores comunes + +Si bien en la mayoría de los casos esto tiene sentido, el primero puede cosiderarse como otro +mal diseño del lenguaje, ya que **nunca** tiene un uso práctico. + + Foo.method = function() { + function test() { + // this es establecido como un objeto global + } + test(); + }; + +Un error común es que `this` dentro de `test` haga referencia a `Foo`, mientras que en +realidad esto **no es así**. + +Con el fin de acceder a `Foo` desde dentro de `test` es necesario crear una variable local +dentro del `método` para referirse a `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Use that instead of this here + } + test(); + }; + +`that` es justo un nombre normal, pero es comúnmente usado para referenciar a `this` +de forma externa. En combinación con [closures](#function.closures), esto puede ser +también usado para pasar `this` como valor. + +### Asignación de métodos + +Otra cosa que **no** funciona en JavaScript son los alias en las funciones, es decir, +**asignar** un método a una variable. + + var test = someObject.methodTest; + test(); + +Debido al primer caso, `test` actúa como una función de llamada; por lo que +`this` dentro de este no estará referido a `someObject`. + +Mientras que la unión de `this` puede parecer una mala idea en un principio, esto es en +realidad lo que hace trabajar a la [herencia de prototipo](#object.prototype). + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Cuando los `métodos` son llamados desde una instancia de `Bar`, `this` se referirá a una +instancia. + + diff --git a/external/JavaScript-Garden/doc/es/index.json b/external/JavaScript-Garden/doc/es/index.json new file mode 100644 index 0000000..1367f52 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/index.json @@ -0,0 +1,68 @@ +{ + "title": "Jardín de JavaScript", + "langTitle": "JavaScript Garden es Español", + "description": "Una guía sobre lo peculiar y defectos de JavaScript.", + "sections": [ + { + "title": "Introducción", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Objetos", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Funciones", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Tipos", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Núcleo", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Otros", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/es/intro/index.md b/external/JavaScript-Garden/doc/es/intro/index.md new file mode 100644 index 0000000..fb2a288 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/intro/index.md @@ -0,0 +1,46 @@ +## Introducción + +**El Jardín de JavaScript** es una guía de documentación acerca de las +partes más peculiares de este lenguaje de programación. Brinda consejos para evitar +los errores más comunes y sutiles, así como problemas de rendimiento y de malas +prácticas que los programadores menos experimentados en JavaScript pueden resolver +en sus esfuerzos por profundizar en el lenguaje. + +El Jardín de JavaScript **no** prentende enseñar JavaScript. +Se recomienda un conocimiento sobre el lenguaje para entender los temas tratados en +esta guía. Con el fin de aprender los conceptos básicos del lenguaje, por favor +diríjase a la excelente [guía][1] de los desarrolladores de Mozilla. + +## Los autores + +Esta guía es el trabajo de dos encantadores usuarios del foro Stack Overflow, +[Ivo Wetzel][3] (Escrito) y [Zhang Yi Jiang][4] (Diseño). + +## Colaboradores + +- [Colaboradores](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## Hosting + +JavaScript Garden es hospedado en GitHub, además [Cramer Development][7] nos apoya +con un sitio espejo en [JavaScriptGarden.info][8]. + +## Licencia + +El Jardín de JavaScript es publicado bajo la [licencia MIT][9] y es hospedado en +[GitHub][10]. Si encuentra algún error o errata por favor publique [una incidencia][11] o +envie un pull request a nuestro repositorio. También nos puede encontrar en la +[sala de chat de JavaScript][12] en Stack Overflow. + +[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/external/JavaScript-Garden/doc/es/object/forinloop.md b/external/JavaScript-Garden/doc/es/object/forinloop.md new file mode 100644 index 0000000..4805868 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/object/forinloop.md @@ -0,0 +1,51 @@ +## El bucle `for in` + +Al igual que el operador `in`, el bucle `for in` también recorre sobre la +cadena de prototipo cuando este se repite en una iteración en las propiedades de un objeto. + +> **Nota:** El bucle `for in` **no** se repetirá en cualquier propiedad que +> tenga atributos `enumerables` asignados a `false`; por ejemplo, la propiedad +> `length` de un array. + + // Envenenamiento en Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // Imprime ambos bar y moo + } + +Dado que no es posible cambiar el comportamiento del bucle `for in` en sí mismo, es +necesario filtrar las propiedades internas no deseadas dentro del bucle, +esto se hace mediante el uso del método [`hasOwnProperty`](#object.hasownproperty) del +`Object.prototype`. + +> **Nota:** Dado que `for in` siempre recorre por completo la cadena de prototipo, +> este se pondrá más lento con cada capa adicional que un objeto herede. + +### Usando `hasOwnProperty` para filtrado + + // Aún es el foo del código de arriba + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Está versión es la única forma correcta de uso. Esto se debe **sólo** al uso de +`hasOwnProperty` que imprimirá `moo`. Cuando `hasOwnProperty` se omita, el código es +propenso a errores en los casos de prototipos nativos - ej. `Object.prototype` - +se ha extendedido. + +Uno de los frameworks más usado que implementa estas funcionalidades es [Prototype][1]. Cuando el +framework es incluido, el bucle `for in` que no utilicen `hasOwnProperty` no podrá garantizar que +se interrumpa. + +### En conclusión + +Se recomienda utilizar **siempre** el uso de `hasOwnProperty`. Nunca debe suponer +ningún entorno donde el código se ejecute, o si los prototipos +nativos han sido extendidos o no. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/es/object/general.md b/external/JavaScript-Garden/doc/es/object/general.md new file mode 100644 index 0000000..da48e13 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/object/general.md @@ -0,0 +1,99 @@ +## Uso de objetos y propiedades + +Todo en JavaScript actúa como un objeto, con las dos únicas excepciones de +[`null`](#core.undefined) y [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Un error muy común es el uso de literales númericos como objetos. +Esto se debe a un error en el parser de JavaScript que intenta analizar la +*notación de puntos* como un literal de punto flotante. + + 2.toString(); // lanza SyntaxError + +Existe un par de soluciones que pueden utilizarse para hacer que los +literales númericos actúen como objetos. + + 2..toString(); // el segundo punto es reconocido correctamente + 2 .toString(); // observe el espacio a la izquierda del punto + (2).toString(); // el número 2 se evalúa primero + +### Objetos como un tipo de datos + +Los objetos en JavaScript también pueden ser utilizados como una Tabla Hash o conocido como [*Hashmap*][1] en inglés, consisten +principalmente en nombres de propiedades, y asignándoles valores a éstas. + +El uso de un objeto literal - con notación `{}` - puede crear un +objeto plano. Este nuevo objeto [heredado](#object.prototype) desde `Object.prototype` +no posee [propiedades propias](#object.hasownproperty) definidas. + + var foo = {}; // un nuevo objeto vacío + + // un nuevo objeto con la propiedad llamada 'test' con el valor 12 + var bar = {test: 12}; + +### Acceso a las propiedades + +Se puede acceder a las propiedades de un objeto de dos maneras, ya sea a través de la +notación de punto o desde la notación de corchetes. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // ¡funciona! + +Ambas notaciones son idénticas en su funcionamiento, la única diferencia es la +notación de corchetes permite el ajuste dinámico de las propiedades, así como +el uso de propiedades que de otro modo daría lugar a error de sintaxis. + +### Eliminando propiedades + +La única manera de eliminar una propiedad desde un objeto es usando el +operador `delete`; establecer la propiedad a `undefined` o `null` solamente +elimina el *valor* asociado a la propiedad, pero no la *key* (valor clave). + + 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]); + } + } + +Los resultados de la salida son `bar undefined` y `foo null` - sólo `baz` ha +sido removido y por lo tanto no aparece en la salida. + +### Notación de Keys + + var test = { + 'case': 'Soy una palabra clave y debo ser anotado como string', + delete: 'Soy una palabra clave también' // lanza SyntaxError + }; + +Las propiedades de los objetos puede ser simbolizados como caracteres planos y como *strings*. Debido +a otro mal diseño del parser de JavaScript, lo anterior es una excepción +de `SyntaxError` antes de ECMAScript 5. + +Este error se produce porque `delete` es una *keyword*; por lo tanto, debe ser +anotado como un *string literal* para asegurarse que será interpretado correctamente +por diversos motores de JavaScript. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/es/object/hasownproperty.md b/external/JavaScript-Garden/doc/es/object/hasownproperty.md new file mode 100644 index 0000000..feb44ae --- /dev/null +++ b/external/JavaScript-Garden/doc/es/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `hasOwnProperty` + +Con el fin de comprobar si un objeto posee una propiedad definida *en sí* mismo y **no** +en algún lugar de su [cadena de prototipo](#object.prototype), es necesario utilizar +el método `hasOwnProperty` ya que todos los objetos herendan de `Object.prototype`. + +> **Nota:** **No** es suficiente con comprobar si una propiedad está `definida`. +> La propiedad bien podría existir, pero su valor sólo pasa a ser definido como +> `undefined`. + +`hasOwnProperty` es la única utilidad en JavaScript que se ocupa de las propiedades +y **no** las salta en la cadena de prototipo. + + // Envenenamiento en 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 + +Sólo `hasOwnProperty` retornará el resultado correcto y esperado, esto es +ensencial cuando se repite una iteración en las propiedades de cualquier objeto. No hay +otra maner de excluir las propiedades que no están definidas en el mismo objeto, pero +en alguna parte de su cadena de prototipo si. + +### `hasOwnProperty` como propiedad + +JavaScript **no** protege el nombre de la propiedad `hasOwnProperty`; de este modo, si existe +la posibilidad de que un objeto tenga una propiedad con el mismo nombre, es necesario utilizar +`hasOwnProperty` como propiedad *externa* con el fin de obtener resultados correctos. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // siempre devolverá false + + // Utilice otro objeto con hasOwnProperty y llamelo con 'this' para asignarlo a foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### En conclusión + +Cuando se necesite comprobar la existencia de una propiedad en un objeto, `hasOwnProperty` es +el **único** método para hacerlo. También se recomienda el uso de `hasOwnProperty` como +parte de un [bucle `for in`](#object.forinloop), esto evitará errores desde +extenciones de [prototipos](#object.prototype) nativos. + diff --git a/external/JavaScript-Garden/doc/es/object/prototype.md b/external/JavaScript-Garden/doc/es/object/prototype.md new file mode 100644 index 0000000..0461d1f --- /dev/null +++ b/external/JavaScript-Garden/doc/es/object/prototype.md @@ -0,0 +1,116 @@ +## Prototipo + +JavaScript no posee en sus características un sistema clásico de herencia, sino que +utiliza un *prototipo* para esto. + +Si bien a menudo se considera uno de los puntos débiles de JavaScript, el +modelo de herencia prototipado es de hecho más poderoso que el modelo clásico. +Por ejemplo, es bastante trivial construir un modelo clásico a partir del modelo prototipado, +mientras que al contrario es una tarea mucho más difícil. + +Debido al hecho que JavaScript es básicamente el único lenguaje que utiliza +ampliamente la herencia prototipada, se necesita algo de tiempo para adaptarse a +las diferencias entre los dos modelos. + +La primera gran diferencia es que la herencia en JavaScript se realiza usando +llamadas de *cadenas de prototipo* (*prototype chains*). + +> **Nota:** Simplemente usando `Bar.prototype = Foo.prototype` dará lugar a dos objetos + > que comparten el **mismo** prototipo. Por lo tanto, los cambios que se realicen en un +> objeto afectará al otro objeto, así, en la mayoría de los casos no es el efecto +> deseado. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Asigna el prototipo de Bar como una nueva instancia de Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Asegura que el constructor sea Bar + Bar.prototype.constructor = Bar; + + var test = new Bar() // crea una nueva instancia de Bar + + // Resultado de cadena de prototipos (prototype chain) + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +En el código anterior, el objeto `test` hereda de `Bar.prototype` y `Foo.prototype`; +por lo tanto, tendrá acceso a la función `method` que se ha definido en `Foo`. +También se tendrá acceso a a la propiedad `value` de la **única** instancia de `Foo` +que compone su prototipo. Es importante tomar en cuenta que `new Bar()` **no** creará una nueva +instancia de `Foo`, pero retornará lo asignado en su prototipo; de este modo, todas las instancias +de `Bar` tendrán que compartir el **mismo** `valor` de la propiedad. + +> **Nota:** **No** utilice `Bar.prototype = Foo`, ya que no apunta al prototipo +> de `Foo`, sino al objeto de la función `Foo`. Así la cadena de prototipo +> cambiará a `Function.prototype` y no a `Foo.prototype`; +> Por lo tanto, el `método` no estará disponible en la cadena de prototipo. + +### Búsqueda de propiedades + +Cuando se accede a las propiedades de un objeto, JavaScript recorre la cadena de +prototipo hacia **arriba** hasta encontrar la propiedad con el nombre solicitado. + +Cuando se llega al final de la cadena - concretamente `Object.prototype` - y aún +no se ha encontrado la propiedad especificada, se retornará un valor +[undefined](#core.undefined) en su lugar. + +### La propiedad prototype + +Aunque la propiedad prototype es usada por el lenguaje para construir la cadena +de prototipos, es posible asignar **cualquier** valor. Aunque los tipos primitivos +serán ignorados cuando se asigne en prototype. + + function Foo() {} + Foo.prototype = 1; // no tendrá efecto + +La asignación de objetos, como se muestra en el ejemplo anterior, funcionará, y permitirá +la creación dinámica de cadena de prototipos. + +### Rendimiento + +El tiempo tomado en la búsqueda de propiedades es alta y la cadena de prototipo puede +presentar un impacto negativo crítico en el rendimiento en partes del código. Además, +si ha tratado de acceder a propiedades que no existen, esto provoca que se recorra la cadena de prototipo completa. + +Además, al recorrer en [iteración](#object.forinloop) las propiedades de un objeto +, **cada** propiedad encontrada en la cadena de prototipo será enumerada. + +### Extensión de prototipos nativos + +Una mala característica que se suele utilizar para extender `Object.prototype` o cualquier +otro prototipo construido. + +Esta técnica es conocida en inglés como [monkey patching][1] y rompe la *encapsulación* del código. +Si bien es utilizado en frameworks como [Prototype][2], todavía no existen buenas razones para adoptarlo o integrarlo +como tipos de dato o como funcionalidad no estándar. + +La **única** razón coherente para extender un prototipo es para adaptarle nuevas +características de los motores JavaScript más modernos; por ejemplo, +[`Array.forEach`][3]. + +### En conclusión + +Se **debe** entender por completo el módelo de herencia prototipado antes de +escribir código complejo que lo utilice. Además, observe la longitud de la +cadena de prototipo y modifíquela si es necesario para evitar posibles problemas de +rendimiento. Con relación a los prototipos nativos, estos **nunca** deben ser extendidos a +menos que sea para mantener la compatibilidad con nuevas características de JavaScript. + +[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/external/JavaScript-Garden/doc/es/other/timeouts.md b/external/JavaScript-Garden/doc/es/other/timeouts.md new file mode 100644 index 0000000..c714645 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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*, which 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 solution, 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 + +Because 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 because 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(1, 2, 3); + }, 1000) + +> **Note:** While it is also possible to use the syntax +> `setTimeout(foo, 1000, 1, 2, 3)`, 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. + +Furthermore, the use of `setInterval` should be avoided because its scheduler is not +blocked by executing JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/es/types/casting.md b/external/JavaScript-Garden/doc/es/types/casting.md new file mode 100644 index 0000000..34d6fd7 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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 an 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/external/JavaScript-Garden/doc/es/types/equality.md b/external/JavaScript-Garden/doc/es/types/equality.md new file mode 100644 index 0000000..f933d49 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/types/equality.md @@ -0,0 +1,71 @@ +## Igualdad y Comparación + +JavaScript posee 2 maneras diferentes para comparar valores entre objetos para comprobar igualdad. + +### El Operador de Igualdad + +El operador de igualdad consiste en 2 signos es igual: `==` + +JavaScript utiliza *tipado débil*. Esto significa que el operador de igualdad +**obliga** una conversión de tipos para poder compararlos. + + "" == "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 + +La tabla anterior muestra los resultados de la conversión de tipos, éste es el motivo principal +de por qué el uso de `==` es ampliamente considerado una mala práctica. Introduce errores +difíciles de identificar debido a la complejidad de sus reglas de conversión. + +Además, existe un impacto en el rendimiento cuando entra en juego la conversión de tipos; +por ejemplo, una cadena debe ser convertida a número antes de poder ser comparada +con otro número. + +### El Operador de Igualdad Estricto + +El operador de igualdad estricto consiste en **tres** signos es igual: `===`: + +Funciona exactamente igual que el operador de igualdad, excepto que el operador de igualdad +estricto **no** utiliza conversión de tipos entre sus operandos. + + "" === "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 + +Los resultados anteriores son mucho más claros y permiten una detección de errores temprana. +Esto permite un código más sólido en cierto grado y también mejora el rendimiento +en el caso que los operandos sean de tipos diferentes. + +### Comparando Objetos + +Aunque `==` como `===` son considerados operadores de **igualdad**, se comportan +de maneras diferentes cuando al menos uno de sus operandos es `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +En este caso, los dos operadores comparan por **referencia** y **no** por igualdad; esto es, +comparan por la misma **instancia** del objeto, parecido +al operador `is` en Python y la comparación entre punteros en C. + +### En Conclusión + +Es altamente recomendable usar sólo el operador de **igualdad estricta**. En los casos +donde los tipos de datos necesitan ser convertidos, debe hacerse [explícitamente](#types.casting) +y no dejárselo a las complicadas reglas de conversión del lenguaje. + diff --git a/external/JavaScript-Garden/doc/es/types/instanceof.md b/external/JavaScript-Garden/doc/es/types/instanceof.md new file mode 100644 index 0000000..8425112 --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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 originate 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 originate from the same JavaScript context. Just like the +[`typeof`](#types.typeof) operator, every other use of it should be **avoided**. + diff --git a/external/JavaScript-Garden/doc/es/types/typeof.md b/external/JavaScript-Garden/doc/es/types/typeof.md new file mode 100644 index 0000000..00377db --- /dev/null +++ b/external/JavaScript-Garden/doc/es/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` because 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**. + + diff --git a/external/JavaScript-Garden/doc/fi/array/constructor.md b/external/JavaScript-Garden/doc/fi/array/constructor.md new file mode 100644 index 0000000..ee3e61b --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/array/constructor.md @@ -0,0 +1,25 @@ +## `Array`-konstruktori + +`Array`-oletuskonstruktorin käytös ei ole lainkaan yksiselitteistä. Tämän vuoksi suositellaankin, että konstruktorin sijasta käytetään literaalinotaatiota `[]`. + + [1, 2, 3]; // Tulos: [1, 2, 3] + new Array(1, 2, 3); // Tulos: [1, 2, 3] + + [3]; // Tulos: [3] + new Array(3); // Tulos: [] + new Array('3') // Tulos: ['3'] + +Mikäli `Array`-konstruktorille annetaan vain yksi argumentti ja se on tyypiltään `Number`, konstruktori palauttaa uuden *harvan* taulukon, jonka `length`-attribuutti on asetettu annetun numeron mukaisesti. On tärkeää huomata, että **ainoastaan** `length` asetetaan tällä tavoin, todellisia taulukon indeksejä ei alusteta. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, indeksiä ei ole alustettu + +Tämä on käytännöllistä vain harvoin, kuten merkkijonon toiston tapauksessa. Tällöin voidaan välttää `for-luupin` käyttämistä. + + new Array(count + 1).join(stringToRepeat); + +### Yhteenveto + +`Array`-konstruktorin käyttöä tulee käyttää niin paljon kuin suinkin mahdollista. Sen sijaan on suositeltavaa käyttää literaalinotaatiota. Literaalit ovat lyhyempiä ja niiden syntaksi on selkeämpi. Tämän lisäksi ne tekevät koodista luettavampaa. + diff --git a/external/JavaScript-Garden/doc/fi/array/general.md b/external/JavaScript-Garden/doc/fi/array/general.md new file mode 100644 index 0000000..238c075 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/array/general.md @@ -0,0 +1,40 @@ +## Taulukon iterointi ja attribuutit + +Vaikka taulukot ovatkin JavaScript-olioita, niiden tapauksessa ei välttämättä kannata käyttää [`for in loop`](#object.forinloop)-luuppia. Pikemminkin tätä tapaa tulee **välttää**. + +> **Huomio:** JavaScript-taulukot **eivät ole** *assosiatiivisia*. JavaScriptissa ainoastaan [oliot](#object.general) ovat avain-arvo-mappauksia. On huomattavaa, että toisin kuin assosiatiiviset taulukot, oliot **eivät** säilytä järjestystään. + +`for in`-luuppi iteroi kaikki prototyyppiketjun sisältämät ominaisuudet. Tämän vuoksi tulee käyttää erityistä [`hasOwnProperty`](#object.hasownproperty)-metodia, jonka avulla voidaan taata, että käsitellään oikeita ominaisuuksia. Tästä johtuen iteroint on jo lähtökohtaisesti jopa **kaksikymmentä** kertaa hitaampaa kuin normaalin `for`-luupin tapauksessa. + +### Iterointi + +Taulukkojen tapauksessa paras suorituskyky voidaan saavuttaa käyttämällä klassista `for`-luuppia. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Edelliseen esimerkkiin liittyy yksi mutta. Listan pituus on tallennettu välimuistiin erikseen käyttämällä `l = list.length`-lauseketta. + +Vaikka `length`-ominaisuus määritelläänkin taulukossa itsessään, arvon hakeminen sisältää ylimääräisen operaation. Uudehkot JavaScript-ympäristöt **saattavat** optimoida tämän tapauksen. Tästä ei kuitenkaan ole mitään takeita. + +Todellisuudessa välimuistin käytön pois jättäminen voi hidastaa luuppia jopa puolella. + +### `length`-ominaisuus + +`length`-ominaisuuden *getteri* palauttaa yksinkertaisesti taulukon sisältämien alkioiden määrän. Sen *setteriä* voidaan käyttää taulukon **typistämiseen**. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Pituuden pienemmäksi asettaminen typistää taulukkoa. Sen kasvattaminen ei kuitenkaan vaikuta mitenkään. + +### Yhteenveto + +Parhaan suorituskyvyn kannalta on parhainta käyttää tavallista `for`-luuppia ja tallentaa `length`-ominaisuus välimuistiin. `for in`-luupin käyttö taulukon tapauksessa on merkki huonosti kirjoitetusta koodista, joka on altis bugeille ja heikolle suorituskyvylle. + diff --git a/external/JavaScript-Garden/doc/fi/core/eval.md b/external/JavaScript-Garden/doc/fi/core/eval.md new file mode 100644 index 0000000..d9b8901 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/core/eval.md @@ -0,0 +1,39 @@ +## Miksi `eval`-funktiota tulee välttää + +`eval` suorittaa JavaScript-koodia sisältävän merkkijonon paikallisessa näkyvyysalueessa. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +`eval` suoritetaan paikallisessa näkyvyysalueessa ainoastaan kun sitä kutsutaan **suorasti** *ja* kutsutun funktion nimi on todellisuudessa `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +`eval`-funktion käyttöä tulee välttää **ehdottomasti**. 99.9% sen "käyttötapauksista" voidaan toteuttaa **ilman** sitä. + +### Piilotettu `eval` + +[Aikakatkaisufunktiot](#other.timeouts) `setTimeout` and `setInterval` voivat kumpikin ottaa merkkijonon ensimmäisenä argumenttinaan. Kyseinen merkkijono suoritetaan **aina** globaalissa näkyvyysalueessa, koska tuolloin `eval`-funktiota kutsutaan epäsuorasti. + +### Turvallisuusongelmat + +`eval` on myös turvallisuusongelma. Se suorittaa **minkä tahansa** sille annetun koodin. Tämän vuoksi sitä ei tule **ikinä** käyttää tuntemattomasta tai epäluotttavasta lähteestä tulevien merkkijonojen kanssa. + +### Yhteenveto + +`eval`-funktiota ei pitäisi käyttää koskaan. Mikä tahansa sitä käyttävä koodi on kyseenalaista sekä suorituskyvyn että turvallisuuden suhteen. Mikäli jokin tarvitsee `eval`-funktiota toimiakseen, tulee sen suunnittelutapa kyseenalaistaa. Tässä tapauksessa on parempi suunnitella toisin ja välttää `eval`-funktion käyttöä. + diff --git a/external/JavaScript-Garden/doc/fi/core/semicolon.md b/external/JavaScript-Garden/doc/fi/core/semicolon.md new file mode 100644 index 0000000..07586f9 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/core/semicolon.md @@ -0,0 +1,100 @@ +## Automaattiset puolipisteet + +Vaikka JavaScript käyttääkin C:n tapaista syntaksia, se **ei** pakota käyttämään puolipisteitä. Niiden käyttöä voidaan halutessa välttää. + +Tästä huolimatta JavaScript ei kuitenkaan ole puolipisteetön kieli. Se tarvitsee niitä ymmärtääkseen lähdekoodia. Tämän vuoksi JavaScript-parseri lisää niitä tarpeen mukaan **automaattisesti**. + + var foo = function() { + } // parsimisvirhe, lisätään puolipiste + test() + +Lisäys tapahtuu ja parseri yrittää uudelleen. + + var foo = function() { + }; // ei virhettä, parsiminen jatkuu + test() + +Automaattista puolipisteiden lisäämistä pidetään eräänä JavaScriptin **suurimmista** suunnitteluvirheistä. Tämä johtuu siitä, että se voi muuttaa tapaa, jolla koodi käyttäytyy. + +### Kuinka se toimii + +Alla oleva koodi ei sisällä puolipisteitä. Täten niiden lisääminen jää parserin tehtäväksi. + + (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) + +Alla parserin arvaus. + + (function(window, undefined) { + function test(options) { + + // Not inserted, lines got merged + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- lisätty + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- lisätty + + return; // <- lisätty, rikkoo return-lauseen + { // kohdellaan lohkona + + // nimike ja yhden lausekkeen lause + foo: function() {} + }; // <- lisätty + } + window.test = test; // <- lisätty + + // Rivit yhdistettiin jälleen + })(window)(function(window) { + window.someLibrary = {}; // <- lisätty + + })(window); //<- lisätty + +> **Huomio:** JavaScript-parseri ei käsittele return-lauseita ja rivivaihtoja "kunnolla". Vaikka tämä ei välttämättä olekaan parserin vika, voi siitä seurata epämiellyttäviä sivuvaikutuksia. + +Yllä olevassa tapauksessa parseri muutti huomattavasti koodin käytöstä. Joissain tapauksissa se tekee kokonaan **väärän asian**. + +### Johtavat sulkeet + +Parseri **ei** lisää puolipistettä johtavien sulkeiden tapauksessa. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Koodi muuttuu seuraavaksi. + + log('testing!')(options.list || []).forEach(function(i) {}) + +On **hyvin** mahdollista, että `log` **ei** palauta funktiota. Tästä johtuen yllä oleva palauttanee `TypeError`-virheen, joka toteaa että `undefined ei ole funktio`. + +### Yhteenveto + +On suositeltavaa ettei puolipisteitä jätetä pois **milloinkaan**. Tämän lisäksi sulut kannattaa pitää niitä vastaavien lausekkeiden kanssa samalla rivillään. `if` ja `else`-lauseiden tapauksessa sulkuja kannattaa käyttää aina. Sen lisäksi että edellä mainitut suositukset tekevät koodista johdonmukaisempaa, estävät ne myös JavaScript-parseria muuttamasta sen käytöstapaa. + diff --git a/external/JavaScript-Garden/doc/fi/core/undefined.md b/external/JavaScript-Garden/doc/fi/core/undefined.md new file mode 100644 index 0000000..148c44d --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/core/undefined.md @@ -0,0 +1,54 @@ +## `undefined` ja `null` + +JavaScript sisältää kaksi erillistä arvoa `ei millekään`. Näistä hyödyllisempti on `undefined`. + +### `undefined` ja sen arvo + +`undefined` on tyyppi, jolla on vain yksi arvo: `undefined`. + +Kieli määrittelee myös globaalin muuttujan, jonka arvo on `undefined`. Myös tätä arvoa kutsutaan nimellä `undefined`. Tämä muuttuja **ei** kuitenkaan ole vakio eikä kielen avainsana. Tämä tarkoittaa siis sitä, että sen *arvo* voidaan ylikirjoittaa. + +> **ES5 Huomio:** ECMAScript 5:ssä `undefined`-tyyppiä ei voida *kirjoittaa* **enää** tiukassa moodissa. Sen nimi voidaan kuitenkin jättää katveeseen määrittelemällä esimerkiksi funktio, jonka nimi on `undefined`. + +Seuraavat tapaukset palauttavat `undefined`-arvon: + + - Globaalin (muokkaamattoman) muuttujan `undefined` arvon haku. + - Puuttuvista `return`-lauseista seuraavat epäsuorat palautusarvot. + - `return`-lauseet, jotka eivät palauta selvästi mitään. + - Olemattomien ominaisuuksien haut. + - Funktioparametrit, joiden arvoa ei ole asetettu. + - Mikä tahansa, joka on asetettu arvoon `undefined`. + +### Arvon `undefined` muutosten hallinta + +Koska globaali muuttuja `undefined` sisältää ainoastaan todellisen `undefined`-tyypin arvon kopion, **ei** sen asettamienn uudelleen muuta *tyypin* `undefined` arvoa. + +Kuitenkin, jotta `undefined`-tyypin arvoa voidaan verrata, tulee sen arvo voida hakea jotenkin ensin. + +Tätä varten käytetään yleisesti seuraavaa tekniikkaa. Ajatuksena on antaa itse arvo käyttäen [nimetöntä käärettä](#function.scopes). + + var undefined = 123; + (function(something, foo, undefined) { + // paikallisen näkyvyysalueen undefined + // voi viitata jälleen todelliseen arvoon + + })('Hello World', 42); + +Samaan lopputuloksen voidaan päästä myös käyttämällä esittelyä kääreen sisällä. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Tässä tapauksessa ainut ero on se, että pakattu versio vie 4 tavua enemmän tilaa 'var'-lauseen vuoksi. + +### `null` ja sen käyttötapaukset + +Vaikka `undefined`-arvoa käytetäänkin usein perinteisen *null*-arvon sijasta, todellinen `null` (sekä literaali että tyyppi) on enemmän tai vähemmän vain tietotyyppi. + +Sitä käytetään joissain JavaScriptin sisäisissä toiminnoissa, kuten prototyyppiketjun pään toteamisessa (`Foo.prototype = null`). Useimmissa tapauksissa se voidaan korvata `undefined`-arvoa käyttäen. + + diff --git a/external/JavaScript-Garden/doc/fi/function/arguments.md b/external/JavaScript-Garden/doc/fi/function/arguments.md new file mode 100644 index 0000000..af70618 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/arguments.md @@ -0,0 +1,94 @@ +## `arguments`-olio + +Jokainen JavaScriptin näkyvyysalue pääsee käsiksi erikoismuuttujaan nimeltään `arguments`. Tämä muuttuja sisältää listan kaikista funktiolle annetuista argumenteista. + +> **Huomio:** Mikäli `arguments` on jo määritelty funktion sisällä joko näkyvyysalueen, `var`-lauseen tai parametrin kautta, `arguments`-oliota ei luoda. + +`arguments`-olio **ei** ole `Array`. Sen semantiikka, erityisesti `length`-ominaisuus, muistuttaa taulukkoa. Tästä huolimatta se ei peri `Array.prototype`:stä ja on itse asiassa `Object`. + +Tästä johtuen `arguments`-olioon **ei** voida soveltaa normaaleja taulukkometodeja, kuten `push`, `pop` tai `slice`. Vaikka iterointi onnistuukin `for`-luuppeja käyttäen, tulee se muuttaa aidoksi `Array`-olioksi ennen kuin siihen voidaan soveltaa näitä metodeja. + +### Array-olioksi muuttaminen + +Alla oleva koodi palauttaa uuden `Array`-olion, joka sisältää `arguments`-olion kaikki jäsenet. + + Array.prototype.slice.call(arguments); + +Tämä muutos on luonteeltaan **hidas** eikä sitä suositella käytettävän suorituskykyä vaativissa osissa koodia. + +### Argumenttien antaminen + +Funktiosta toiselle voidaan antaa argumentteja seuraavasti. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // tee jotain + } + +Toinen keino on käyttää sekä `call`- että `apply`-funktioita yhdessä ja luoda nopeita, sitomattomia kääreitä. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Luo "metodin" sitomaton versio + // Se ottaa seuraavat parametrit: this, arg1, arg2...argN + Foo.method = function() { + + // Tulos: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Muodolliset parametrit ja argumenttien indeksit + +`arguments`-olio luo sekä *getter*- että *setter*-funktiot sekä sen ominaisuuksille että myös funktion muodollisille parametreille. + +Tästä seuraa, että muodollisen parametrin arvon muuttaminen muuttaa myös `arguments`-olion vastaavan ominaisuuden arvoa ja toisin päin. + + 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); + +### Suorituskykyyn liittyviä myyttejä ja totuuksia + +`arguments`-olio luodaan aina paitsi jos se on jo julistettu nimenä funktiossa tai sen muodollisena parametrina. Tämä siitä huolimatta käytetäänkö sitä vai ei. + +Sekä *getter*- ja *setter*-funktiot luodaan **aina**. Tästä seuraa, että niiden käytöllä ei ole juurikaan merkitystä suorituskyvyn kannalta. + +> **ES5 Huomio:** Näitä *getter*- ja *setter*-funktioita ei luoda tiukassa moodissa. + +On kuitenkin eräs tapaus, jossa suorituskyky kärsii. Tämä liittyy `arguments.callee`-ominaisuuden käyttöön. + + function foo() { + arguments.callee; // tee jotain tällä funktio-oliolla + arguments.callee.caller; // ja kutsuvalla funktio-oliolla + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // normaalisti tämä olisi inline-optimoitu + } + } + +Yllä olevassa koodissa `foo`-kutsua ei voida [käsitellä avoimesti][1], koska sen tulee tietää sekä itsestään että kutsujasta. Sen lisäksi, että se haittaa suorituskykyä, rikkoo se myös kapseloinnin. Tässä tapauksessa funktio voi olla riippuvainen tietystä kutsuympäristöstä. + +On **erittäin suositeltavaa** ettei `arguments.callee`-ominaisuutta tai sen ominaisuuksia käytetä **ikinä**. + +> **ES5 Huomio:** Tiukassa moodissa `arguments.callee` palauttaa `TypeError`-virheen, koska se käyttö on vanhennettu. + +[1]: http://en.wikipedia.org/wiki/Inlining + diff --git a/external/JavaScript-Garden/doc/fi/function/closures.md b/external/JavaScript-Garden/doc/fi/function/closures.md new file mode 100644 index 0000000..2e0a227 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/closures.md @@ -0,0 +1,78 @@ +## Sulkeumat ja viitteet + +*Sulkeumat* ovat eräs JavaScriptin voimakkaimmista ominaisuuksista. Näkyvyysalueilla on siis **aina** pääsy ulompaan näkyvyysalueeseensa. Koska JavaScriptissä ainut tapa määritellä näkyvyyttä pohjautuu [funktionäkyvyyteen](#function.scopes), kaikki funktiot käyttäytyvät oletuksena sulkeumina. + +### Paikallisten muuttujien emulointi + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Tässä tapauksessa `Counter` palauttaa **kaksi** sulkeumaa. Funktion `increment` lisäksi palautetaan myös funktio `get`. Kumpikin funktio **viittaa** `Counter`-näkyvyysalueeseen ja pääsee siten käsiksi `count`-muuttujan arvoon. + +### Miksi paikalliset muuttujat toimivat + +JavaScriptissä ei voida viitata näkyvyysalueisiin. Tästä seuraa **ettei** `count`-muuttujan arvoon voida päästä käsiksi funktion ulkopuolelta. Ainoastaan nämä kaksi sulkeumaa mahdollistavat sen. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +Yllä oleva koodi **ei** muuta muuttujan `count` arvoa `Counter`-näkyvyysalueessa. Tämä johtuu siitä, että `foo.hack`-ominaisuutta ei ole määritelty **kyseisessä** näkyvyysalueessa. Sen sijaan se luo - tai ylikirjoittaa - *globaalin* muuttujan `count`. + +### Sulkeumat luupeissa + +Usein sulkeumia käytetään väärin luuppien sisällä indeksimuuttujien arvon kopiointiin. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Yllä oleva koodi **ei** tulosta numeroita `nollasta` `yhdeksään`. Sen sijaan se tulostaa numeron `10` kymmenen kertaa. + +*Nimetön* funktio saa **viitteen** `i`-muuttujaan `console.log`-kutsuhetkellä. Tällöin luuppi on jo suoritettu ja `i`:n arvoksi on asetettu `10`. + +Päästäksemme haluttuun lopputulokseen on tarpeen luoda **kopio** `i`:n arvosta. + +### Viiteongelman välttäminen + +Voimme välttää ongelman käyttämällä [nimetöntä käärettä](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +Nimetöntä ulkofunktiota kutsutaan heti käyttäen `i`:tä se ensimmäisenä argumenttina. Tällöin se saa kopion `i`:n **arvosta** parametrina `e`. + +Nimetön funktio, jolle annetaan `setTimeout` sisältää nyt viitteen `e`:hen, jonka arvoa luuppi **ei** muuta. + +Samaan lopputulokseen voidaan päästä myös palauttamalla funktio nimettömästä kääreestä. Tällöin se käyttäytyy samoin kuten yllä. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/external/JavaScript-Garden/doc/fi/function/constructors.md b/external/JavaScript-Garden/doc/fi/function/constructors.md new file mode 100644 index 0000000..87da08e --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/constructors.md @@ -0,0 +1,101 @@ +## Konstruktorit + +JavaScriptin konstruktorit eroavat monista muista kielistä selvästi. Jokainen funktiokutsu, joka sisältää avainsanan `new` toimii konstruktorina. + +Konstruktorin - kutsutun funktion - `this`-muuttujan arvo viittaa luotuun `Object`-olioon. Tämän **uuden** olion [`prototyyppi`](#object.prototype) asetetaan osoittamaan konstruktorin kutsuman funktio-olion prototyyppiin. + +Mikäli kutsuttu funktio ei sisällä selvää `return`-lausetta, tällöin se palauttaa `this`-muuttujan arvon eli uuden olion. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +Yllä `Foo`:ta kutsutaan konstruktorina. Juuri luodun olion `prototyyppi` asetetaan osoittamaan ominaisuuteen `Foo.prototype`. + +Selvän `return`-lausekkeen tapauksessa funktio palauttaa ainoastaan määritellyn lausekkeen arvon. Tämä pätee tosin **vain jos** palautettava arvo on tyypiltään `Object`. + + function Bar() { + return 2; + } + new Bar(); // uusi olio + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // palautettu olio + +Mikäli `new`-avainsanaa ei käytetä, funktio **ei** palauta uutta oliota. + + function Foo() { + this.bla = 1; // asetetaan globaalisti + } + Foo(); // undefined + +Vaikka yllä oleva esimerkki saattaa näyttää toimivan joissain tapauksissa, viittaa [`this`](#function.this) globaalin olion `this`-ominaisuuteen. + +### Tehtaat + +Mikäli `new`-avainsanan käyttöä halutaan välttää, voidaan konstruktori pakottaa palauttamaan arvo. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Tässä tapauksessa molemmat `Bar`-funktion kutsut käyttäytyvät samoin. Kumpikin kutsu palauttaa olion, joka sisältää `method`-ominaisuuden. Kyseinen ominaisuus on [sulkeuma](#function.closures). + +On myös tärkeää huomata, että kutsu `new Bar()` **ei** vaikuta palautetun olion prototyyppiin. Vaikka luodun olion prototyyppi onkin asetettu, `Bar` ei palauta ikinä kyseistä prototyyppioliota. + +Yllä olevassa esimerkissä `new`-avainsanan käytöllä tai käyttämällä jättämisellä ei ole toiminnan kannalta mitään merkitystä. + +### Tehtaiden käyttö uusien olioiden luomiseen + +Usein suositellaan `new`-avainsanan käytön **välttämistä**. Tämä johtuu siitä, että sen käyttämättä jättäminen voi johtaa bugeihin. + +Sen sijaan suositellaan käytettävän tehdasta, jonka sisällä varsinainen olio konstruoidaan. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +Vaikka yllä oleva esimerkki välttää `new`-avainsanan käyttöä ja tekee [paikallisten muuttujien](#function.closures) käytön helpommaksi, sisältää se joitain huonoja puolia. + + 1. Se käyttää enemmän muistia. Tämä johtuu siitä, että luodut oliot **eivät** jaa prototyypin metodeja. + 2. Perinnän tapauksessa tehtaan tulee kopioida toisen olion kaikki metodit tai vaihtoehtoisesti asettaa kyseinen olio toisen prototyypiksi. + 3. Prototyyppiketjun käsitteen unohtaminen on vain välttääksemme `new`-avainsanan käyttöä on vastoin kielen filosofista perustaa. + +### Yhteenveto + +Vaikka `new`-avainsanan käyttö voi johtaa bugeihin, prototyyppien käyttöä **ei** kannata unohtaa kokonaan. Loppujen lopuksi kyse on siitä, kumpi tapa sopii sovelluksen tarpeisiin paremmin. On erityisen tärkeää valita jokin tietty tapa ja **pitäytyä** sen käytössä. + diff --git a/external/JavaScript-Garden/doc/fi/function/general.md b/external/JavaScript-Garden/doc/fi/function/general.md new file mode 100644 index 0000000..2e03822 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/general.md @@ -0,0 +1,38 @@ +## Funktiomääreet ja lausekkeet + +JavaScriptissä funktiot ovat ensimmäisen luokan olioita. Tämä tarkoittaa sitä, että niitä voidaan välittää kuten muitakin arvoja. Usein tätä käytetään takaisinkutsuissa käyttämällä *nimettömiä, mahdollisesti asynkronisia funktioita*. + +### `function`-määre + + function foo() {} + +Yllä oleva funktio [hilataan](#function.scopes) ennen ohjelman suorituksen alkua. Se näkyy *kaikkialle* näkyvyysalueessaan, jossa se on *määritelty*. Tämä on totta jopa silloin, jos sitä kutsutaan ennen määrittelyään. + + foo(); // Toimii, koska foo on luotu ennen kuin koodi suoritetaan + function foo() {} + +### `function`-lauseke + + var foo = function() {}; + +Tämä esimerkki asettaa nimeämättömän ja *nimettömän* funktion muuttujan `foo` arvoksi. + + foo; // 'undefined' + foo(); // tämä palauttaa TypeError-virheen + var foo = function() {}; + +`var` on määre. Tästä johtuen se hilaa muuttujanimen `foo` ennen kuin itse koodia ryhdytään suorittamaan. + +Sijoituslauseet suoritetaan *vasta* kun niihin saavutaan. Tästä johtuen `foo` saa arvokseen [undefined](#core.undefined) ennen kuin varsinaista sijoitusta päästään suorittamaan. + +### Nimetty funktiolauseke + +Nimettyjen funktioiden sijoitus tarjoaa toisen erikoistapauksen. + + var foo = function bar() { + bar(); // Toimii + } + bar(); // ReferenceError + +Tässä tapauksessa `bar` ei ole saatavilla ulommalla näkyvyysalueessa. Tämä johtuu siitä, että se on sidottu `foo`:n sisälle. Tämä johtuu siitä, kuinka näkyvyysalueet ja niihin kuuluvat jäsenet [tulkitaan](#function.scopes). Funktion nimi on *aina* saatavilla sen paikallisessa näkyvyysalueessa itsessään. + diff --git a/external/JavaScript-Garden/doc/fi/function/scopes.md b/external/JavaScript-Garden/doc/fi/function/scopes.md new file mode 100644 index 0000000..be7b56d --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/scopes.md @@ -0,0 +1,190 @@ +## Näkyvyysalueet ja nimiavaruudet + +Vaikka JavaScript-käyttääkin aaltosulkeita blokkien ilmaisuun, se **ei** tue blokkinäkyvyyttä. Tämä tarkoittaa sitä, että kieli tukee ainoastaan *funktionäkyvyyttä. + + function test() { // näkyvyysalue + for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue + // count + } + console.log(i); // 10 + } + +> **Huomio:** Mikäli `return`-lausetta ei käytetä sijoitukseen, `{...}`-notaatio tulkitaan blokkina **eikä** olioliteraalina. Tästä ja [puolipisteiden automaattisesta lisäämisestä](#core.semicolon] seuraa yllättäviä virheitä. + +JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena *globaalissa* nimiavaruudessa. + +Joka kerta kun muuttujaan viitataan, JavaScript käy kaikki näkyvyysalueet läpi alhaalta lähtien. Mikäli se saavuttaa globaalin näkyvyystalueen, eikä löydä haettua nimeä, se palauttaa `ReferenceError`-virheen. + +### Riesa nimeltä globaalit muuttujat + + // skripti A + foo = '42'; + + // skripti B + var foo = '42' + +Yllä olevat skriptit käyttäytyvät **eri** tavoin. Skripti A määrittelee muuttujan nimeltä `foo` *globaalissa* näkyvyysalueessa. Skripti B määrittelee `foo`-muuttujan *vallitsevassa* näkyvyysalueessa. + +Tämä **ei** ole **sama asia**. `var`-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin. + + // globaali näkyvyysalue + var foo = 42; + function test() { + // paikallinen näkyvyysalue + foo = 21; + } + test(); + foo; // 21 + +`var`-avainsanan pois jättäminen johtaa siihen, että funktio `test` ylikirjoittaa `foo`:n arvon. Vaikka tämä ei välttämättä vaikutakaan suurelta asialta, tuhansien rivien tapauksessa `var`-avainsanan käyttämättömyys voi johtaa vaikeasti löydettäviin bugeihin. + + // globaali näkyvyysalue + var items = [/* joku lista */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // aliluupin näkyvyysalue + for(i = 0; i < 10; i++) { // hups, var jäi pois + // jotain makeaa ja hienoa + } + } + +Tässä tapauksessa ulomman luupin suoritus lopetetaan ensimmäisen `subLoop`-kutsun jälkeen. Tämä johtuu siitä, että se ylikirjoittaa `i`:n globaalin arvon. Mikäli jälkimmäisessä luupissa olisi käytetty `var`-avainsanaa, olisi ikävyyksiltä vältytty. `var`-avainsanaa ei siis tule **ikinä** jättää pois ellei siihen ole *hyvää syytä*. + +### Paikalliset muuttujat + +Ainoastaan [funktion](#function.general) parametrit ja muuttujat, jotka sisältävät `var`-määreen ovat paikallisia. + + // globaali näkyvyysalue + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // paikallinen näkyvyysalue + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` ja `i` ovatkin `test`-funktiolle paikallisia. `bar` sijoitus muuttaa globaalin muuttujan arvoa. + +### Hilaaminen + +JavaScript **hilaa** määreitä. Tämä tarkoittaa sitä, että sekä `var`-lausekkeet että `function`-määreet siirretään ne sisältävän näkyvyysalueen huipulle. + + 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]; + } + } + +Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää `var`-lausekkeet ja `function`-määreet lähimmän näkyvyysalueen huipulle. + + // var-lausekkeet siirrettiin tänne + var bar, someValue; // oletuksena 'undefined' + + // myös funktio-määre siirtyi tänne + function test(data) { + var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // TypeError-virhe, baria ei ole vielä määritelty + someValue = 42; // hilaus ei koske sijoituksia + bar = function() {}; + + test(); + +Sen lisäksi, että puuttuva blokkinäkyvyys siirtää `var`-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä `if`-rakenteista vaikeita käsittää. + +Alkuperäisessä koodissa `if`-lause näytti muokkaavan *globaalia muuttujaa* `goo`. Todellisuudessa se muokkaa *paikallista muuttujaa* varsinaisen hilauksen jälkeen. + +Seuraava koodi saattaisi ensi näkemältä aiheuttaa `ReferenceError`-virheen. Näin ei kuitenkaan tapahdu *hilauksen* ansiosta. + + // onko SomeImportantThing alustettu + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Tämä toimii, koska `var`-lauseke on hilattu *globaalin näkyvyysalueen* huipulle. + + var SomeImportantThing; + + // mahdollista alustuskoodia + + // onhan se alustettu + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Nimienerottelujärjestys + +Kaikki JavaScriptin näkyvyysalueet, *globaalin näkyvyysalue* mukaanlukien, sisältävät erikoismuuttujan [`this`](#function.this). `this` viittaa *tämänhetkiseen olioon*. + +Funktioiden näkyvyysalueet sisältävät myös [`arguments`](#function.arguments)-olion. Se sisältää funktiolle annetut argumentit. + +Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi `foo`:n arvoon JavaScript käyttäytyy seuraavasti: + + 1. Mikäli `var foo`-lauseke löytyy tämänhetkisestä näkyvyysalueesta, käytä sen arvoa. + 2. Mikäli eräs funktion parametreista on `foo`, käytä sitä. + 3. Mikäli funktion nimi itsessään on `foo`, käytä sitä. + 4. Siirry ulompaan näkyvyysalueeseen ja suorita **#1** uudelleen. + +> **Huomio:** Mikäli funktio sisältää `arguments`-nimisen parametrin, estää se `arguments`-olion luonnin kokonaan. + +### Nimiavaruudet + +Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä *nimettömiä kääreitä*. + + (function() { + // "nimiavaruus" itsessään + + window.foo = function() { + // paljastettu sulkeuma + }; + + })(); // suorita funktio heti + +Nimettömiä funktioita pidetään [lauseina](#function.general). Jotta niitä voidaan kutsua, tulee ne suorittaa ensin. + + ( // suorita sulkeiden sisältämä funktio + function() {} + ) // ja palauta funktio-olio + () // kutsu suorituksen tulosta + +Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen. + + // Kaksi muuta tapaa + +function(){}(); + (function(){}()); + +### Yhteenveto + +On suositeltavaa käyttää *nimettömiä kääreitä* nimiavaruuksina. Sen lisäksi, että se suojelee koodia nimitörmäyksiltä, se tarjoaa keinon jaotella ohjelma paremmin. + +Globaalien muuttujien käyttöä pidetään yleisesti **huonona tapana**. **Mikä tahansa** niiden käyttö viittaa huonosti kirjoitettuun, virheille alttiiseen ja hankalasti ylläpidettävään koodiin. + diff --git a/external/JavaScript-Garden/doc/fi/function/this.md b/external/JavaScript-Garden/doc/fi/function/this.md new file mode 100644 index 0000000..3e2b09d --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/function/this.md @@ -0,0 +1,91 @@ +## Kuinka `this` toimii + +JavaScripting `this` toimii eri tavoin kuin useimmissa kielissä. Tarkalleen ottaen on olemassa **viisi** eri tapaa, joiden mukaan sen arvo voi määrittyä. + +### Globaali näkyvyysalue + + this; + +Kun `this`-muuttujaa käytetään globaalissa näkyvyysalueessa, viittaa se *globaaliin* olioon. + +### Funktiokutsu + + foo(); + +Tässä tapauksessa `this` viittaa jälleen *globaaliin* olioon. + +> **ES5 Huomio:** Globaalia tapausta ei ole **enää** olemassa, kun käytetään tiukkaa moodia. Sen sijaan `this` saa arvon `undefined`. + +### Metodikutsu + + test.foo(); + +Tässä esimerkissä `this` viittaa `test`-olioon. + +### Konstruktorikutsu + + new foo(); + +Funktiokutsu, jota edeltää `new`-avainsana toimii [konstruktorina](#function.constructors). Funktion sisällä `this` viittaa *juuri luotuun* `Object`-olioon. + +### `this`-arvon asettaminen + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // taulukko laajenee alla olevaksi + foo.call(bar, 1, 2, 3); // tuloksena a = 1, b = 2, c = 3 + +`Function.prototype`-olion `call`- ja `apply`-metodeita käytettäessä `this`-ominaisuuden arvo määrittyy ensimmäisen annetun argumentin perusteella. + +Seurauksena `foo`-funktion sisältämä `this` asettuu `bar`-olioon toisin kuin perustapauksessa. + +> **Huomio:** `this` **ei voi** viitata `Object`-literaalin sisältämään olioon. Tästä seuraa, että `var obj = {me: this}` tapauksessa `me` **ei** viittaa `obj`-olioon. `this`-arvo määrittyy ainoastaan listatuissa viidessä tapauksessa. + +### Yleisiä ongelmakohtia + +Useimmat näistä tapauksista ovat järkeviä. Ensimmäistä niistä tosin voidaan pitää suunnitteluvirheenä, jolle ei ole mitään järkevää käyttöä **ikinä**. + + Foo.method = function() { + function test() { + // this asettuu globaaliin olioon + } + test(); + }; + +Yleisesti luullaan, että test-funktion sisältämä `this` viittaa tässä tapauksessa `Foo`-olioon. Todellisuudessa se **ei** kuitenkaan tee näin. + +Jotta `Foo`-olioon voidaan päästä käsiksi `test`-funktion sisällä, tulee metodin sisälle luoda paikallinen muuttuja, joka viittaa `Foo`-olioon. + + Foo.method = function() { + var that = this; + function test() { + // Käytä thatia thissin sijasta + } + test(); + }; + +`that` on normaali nimi, jota käytetään yleisesti viittaamaan ulompaan `this`-muuttujaan. [Sulkeumia](#function.closures) käytettäessä `this`-arvoa voidaan myös välittää edelleen. + +### Metodien sijoittaminen + +JavaScriptissä funktioita **ei** voida nimetä uudelleen eli siis sijoittaa **edelleen**. + + var test = someObject.methodTest; + test(); + +Ensimmäisestä tapauksesta johtuen `test` toimii kuten normaali funktiokutsu; tällöin sen sisältämä `this` ei enää osoita `someObject`-olioon. + +Vaikka `this`-arvon myöhäinen sidonta saattaa vaikuttaa huonolta idealta, se mahdollistaa [prototyyppeihin pohjautuvan perinnän](#object.prototype). + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Kun `method`-metodia kutsutaan `Bar`-oliossa, sen `this` viittaa juurikin tuohon olioon. + + diff --git a/external/JavaScript-Garden/doc/fi/index.json b/external/JavaScript-Garden/doc/fi/index.json new file mode 100644 index 0000000..cee13c4 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/index.json @@ -0,0 +1,73 @@ +{ + "title": "JavaScript-puutarha", + "langTitle": "JavaScript-puutarha suomeksi", + "description": "Opas JavaScriptin outouksiin ja vikoihin ", + "sections": [ + { + "title": "Johdanto", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "translators", + "license" + ] + }, + { + "title": "Oliot", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Funktiot", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Taulukot", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Tyypit", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Ydin", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Muuta", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/fi/intro/authors.md b/external/JavaScript-Garden/doc/fi/intro/authors.md new file mode 100644 index 0000000..0d55751 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/intro/authors.md @@ -0,0 +1,8 @@ +## Tekijät + +Tämä opas pohjautuu kahden mukavan [Stack Overflow][3] käyttäjän työhön. He ovat [Ivo Wetzel][1] (kirjoittaminen) sekä [Zhang Yi Jiang][2] (ulkoasu). + +[1]: http://stackoverflow.com/users/170224/ivo-wetzel +[2]: http://stackoverflow.com/users/313758/yi-jiang +[3]: http://stackoverflow.com/ + diff --git a/external/JavaScript-Garden/doc/fi/intro/contributors.md b/external/JavaScript-Garden/doc/fi/intro/contributors.md new file mode 100644 index 0000000..cda4a6f --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/intro/contributors.md @@ -0,0 +1,3 @@ +## Osallistujat + +- [Osallistujat](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) diff --git a/external/JavaScript-Garden/doc/fi/intro/index.md b/external/JavaScript-Garden/doc/fi/intro/index.md new file mode 100644 index 0000000..e9100fb --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/intro/index.md @@ -0,0 +1,8 @@ +## Johdanto + +**JavaScript-puutarha** sisältää kasvavan kokoelman JavaScriptin ongelmallisiin osiin liittyvää dokumentaatiota. Se tarjoaa vinkkejä, joiden avulla välttää yleisiä virheitä, bugeja sekä suorituskykyongelmia ja huonoja tapoja, joita aloittelevat JavaScript-ohjelmoijat saattavat kohdata kieleen tutustuessaan. + +JavaScript-puutarha **ei** tähtää itse kielen opettamiseen. On suositeltavaa, että lukija ymmärtää jo kielen perusteet ennen itse tekstin lukemista. Nämä perusteet voit oppia esimerkiksi perehtymällä Mozilla Developer Networkin erinomaiseen [oppaaseen][1]. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/external/JavaScript-Garden/doc/fi/intro/license.md b/external/JavaScript-Garden/doc/fi/intro/license.md new file mode 100644 index 0000000..09ed4d4 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/intro/license.md @@ -0,0 +1,9 @@ +## Lisenssi + +JavaScript-puutarha on julkaistu [MIT-lisenssin][1]-alaisena ja se on saatavilla [GitHubissa][2]. Mikäli löydät virheitä, lisää se [seurantajärjestelmään][3] tai tee `pull`-pyyntö. Löydät meidät myös [JavaScript huoneesta][4] Stack Overflown chatista. + +[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 + diff --git a/external/JavaScript-Garden/doc/fi/intro/translators.md b/external/JavaScript-Garden/doc/fi/intro/translators.md new file mode 100644 index 0000000..62ae637 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/intro/translators.md @@ -0,0 +1,5 @@ +## Kääntäjät + + - [Juho Vepsäläinen][1] + +[1]: https://github.com/bebraw diff --git a/external/JavaScript-Garden/doc/fi/object/forinloop.md b/external/JavaScript-Garden/doc/fi/object/forinloop.md new file mode 100644 index 0000000..15c9fc6 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/object/forinloop.md @@ -0,0 +1,37 @@ +## `for in`-luuppi + +Aivan kuten `in`-operaattori, myös `for in`-luuppi käy olion prototyyppiketjun läpi iteroidessaan sen ominaisuuksia. + +> **Huomio:** `for in`-luuppi **ei** iteroi ominaisuuksia, joiden `enumerable`-attribuutti on asetettu arvoon `false`. Eräs esimerkki tästä on taulukon `length`-ominaisuus. + + // Object.prototypen myrkyttäminen + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // tulostaa sekä bar että moo + } + +Koska `for in`-luupin käytöstapaa ei voida muokata suoraan, tulee ei-halutut ominaisuudet karsia itse luupin sisällä. Tämä on mahdollista käyttäen `Object.prototype`-olion [`hasOwnProperty`](#object.hasownproperty)-metodia. + +> **Huomio:** `for in`-luupin suorittaminen hidastuu sitä enemmän, mitä pidempi olion prototyyppiketju on. Tämä johtuu siitä, että se joutuu käymään koko ketjun sisällön läpi. + +### `hasOwnProperty`-metodin käyttäminen karsimiseen + + // foo kuten yllä + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Tämä versio on ainut oikea. Se tulostaa **ainoastaan** `moo`, koska se käyttää `hasOwnProperty`-metodia oikein. Kun se jätetään pois, on koodi altis virheille tapauksissa, joissa prototyyppejä, kuten `Object.prototype`, on laajennettu. + +[Prototype][1] on eräs yleisesti käytetty ohjelmointialusta, joka tekee näin. Kun kyseistä alustaa käytetään, `for in`-luupit, jotka eivät käytä `hasOwnProperty`-metodia, menevät varmasti rikki. + +### Yhteenveto + +On suositeltavaa käyttää **aina** `hasOwnProperty`-metodia. Ei ole kannattavaa tehdä ajoympäristöön tai prototyyppeihin liittyviä oletuksia. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/fi/object/general.md b/external/JavaScript-Garden/doc/fi/object/general.md new file mode 100644 index 0000000..6a0e613 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/object/general.md @@ -0,0 +1,82 @@ +## Olioiden käyttö ja ominaisuudet + +Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä oliomaisesti. Nämä poikkeukset ovat [`null`](#core.undefined) sekä [`undefined`](#core.undefined). + + false.toString(); // epätosi + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Yleisesti luullaan ettei numeroliteraaleja voida käyttää olioina. Tämä johtuu viasta JavaScriptin parserissa. Se yrittää parsia numeron *pistenotaatiota* liukulukuliteraalina. + + 2.toString(); // palauttaa SyntaxError-virheen + +Tämä voidaan välttää esimerkiksi seuraavasti. + + 2..toString(); // toinen piste tunnistuu oikein + 2 .toString(); // huomaa pisteen vasemmalla puolen oleva väli + (2).toString(); // 2 arvioidaan ensi + +### Oliot tietotyyppinä + +JavaScriptin olioita voidaan käyttää myös [*hajautustauluna*][1], koska ne muodostavat pääasiassa avaimien ja niihin liittyvien arvojen välisen mappauksen. + +Olioliteraalinotaatiota - `{}` - käyttäen voidaan luoda tyhjä olio. Tämä olio [perii](#object.prototype) `Object.prototype`-olion eikä sille ole määritelty [omia ominaisuuksia](#object.hasownproperty). + + var foo = {}; // uusi, tyhjä olio + + // uusi, tyhjä olio, joka sisältää ominaisuuden 'test' arvolla 12 + var bar = {test: 12}; + +### Pääsy ominaisuuksiin + +Olion ominaisuuksiin voidaan päästä käsiksi kahta eri tapaa käyttäen. Siihen voidaan käyttää joko piste- tai hakasulkunotaatiota. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // toimii + +Kumpikin notaatio toimii samalla tavoin. Ainut ero liittyy siihen, että hakasulkunotaation avulla ominaisuuksien arvoja voidaan asettaa dynaamisesti. Se sallii myös muuten hankalien, virheeseen johtavien nimien käyttämisen. + +### Ominaisuuksien poistaminen + +Ainut tapa poistaa olion ominaisuus on käyttää `delete`-operaattoria. Ominaisuuden asettaminen joko arvoon `undefined` tai `null` poistaa vain siihen liittyneen arvon muttei itse *avainta*. + + 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]); + } + } + +Yllä oleva koodi tulostaa sekä `both undefined` että `foo null`. Ainoastaan `baz` on poistettu. Täten sitä ei myöskään näy tulosteessa. + +### Avainnotaatio + + var test = { + 'case': 'Olen avainsana, joten minun tulee olla merkkijono', + delete: 'Myös minä olen avainsana' // palauttaa SyntaxError-virheen + }; + +Olioiden ominaisuuksia voidaan notatoida käyttäen joko pelkkiä merkkejä tai merkkijonoja. Toisesta JavaScriptin suunnitteluvirheestä johtuen yllä oleva koodi palauttaa `SyntaxError`-virheen ECMAScript 5:ttä edeltävissä versioissa. + +Tämä virhe johtuu siitä, että `delete` on *avainsana*. Täten se tulee notatoida *merkkijonona*. Tällöin myös vanhemmat JavaScript-tulkit ymmärtävät sen oikein. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/fi/object/hasownproperty.md b/external/JavaScript-Garden/doc/fi/object/hasownproperty.md new file mode 100644 index 0000000..9371dca --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/object/hasownproperty.md @@ -0,0 +1,41 @@ +## `hasOwnProperty` + +Jotta voimme tarkistaa onko olion ominaisuus määritelty siinä *itsessään*, tulee käyttää erityistä `Object.prototype`-oliosta periytyvää `hasOwnProperty`-metodia. Tällä tavoin vältämme [prototyyppiketjun](#object.prototype) sisältämät ominaisuudet. + +> **Huomio:** **Ei** riitä tarkistaa vain että ominaisuuden arvo on `undefined`. Ominaisuus voi hyvinkin olla olemassa. Sen arvoksi on vain satuttu asettamaan `undefined`. + +`hasOwnProperty` on ainut JavaScriptin sisältämä metodi, joka käsittelee ominaisuuksia **eikä** käy prototyyppiketjun sisältöä läpi. + + // Object.prototypen myrkyttäminen + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // tosi + + foo.hasOwnProperty('bar'); // epätosi + foo.hasOwnProperty('goo'); // tosi + +Ainoastaan `hasOwnProperty` palauttaa oikean ja odotetun tuloksen. Sen tietäminen on olennaista minkä tahansa olion ominaisuuksia iteroidessa. Tämä on **ainut** tapa löytää olion itsensä ominaisuudet prototyyppiketjusta riippumatta. + +### `hasOwnProperty` ominaisuutena + +JavaScript **ei** suojele `hasOwnProperty`-metodin nimeä. Täten on mahdollista, että olio voi sisältää samannimisen ominaisuuden. Jotta voimme saada oikeita tuloksia, tulee sen sijaan käyttää *ulkoista* `hasOwnProperty`-metodia. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Olkoon vaikka lohikäärmeitä' + }; + + foo.hasOwnProperty('bar'); // palauttaa aina epätoden + + // Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla + // 'this' foohon + ({}).hasOwnProperty.call(foo, 'bar'); // tosi + +### Yhteenveto + +Mikäli pitää selvittää kuuluuko ominaisuus olioon vai ei, **ainoastaan** `hasOwnProperty` voi kertoa sen. Tämän lisäksi on suositeltavaa käyttää `hasOwnProperty`-metodia osana **jokaista** [`for in`-luuppia](#object.forinloop). Tällä tavoin voidaan välttää natiivien [prototyyppien](#object.prototype) laajentamiseen liittyviä ongelmia. + diff --git a/external/JavaScript-Garden/doc/fi/object/prototype.md b/external/JavaScript-Garden/doc/fi/object/prototype.md new file mode 100644 index 0000000..f2497e0 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/object/prototype.md @@ -0,0 +1,80 @@ +## Prototyyppi + +JavaScript ei sisällä klassista perintämallia. Sen sijaan se käyttää *prototyyppeihin* pohjautuvaa ratkaisua. + +Usein tätä pidetään JavaScriptin eräänä suurimmista heikkouksista. Itse asiassa prototyyppipohjainen perintämalli on voimakkaampi kuin klassinen malli. Sen avulla voidaan mallintaa klassinen malli melko helposti. Toisin päin mallintaminen on huomattavasti vaikeampaa. + +JavaScript on käytännössä ainut laajasti käytetty kieli, joka tarjoaa tuen prototyyppipohjaiselle perinnälle. Tästä johtuen mallien väliseen eroon tottuminen voi viedä jonkin akaa. + +Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä se pohjautuu erityisiin *prototyyppiketjuihin*. + +> **Huomio:** Ainoastaan `Bar.prototype = Foo.prototype` johtaa siihen, että molemmat oliot jakavat **saman** prototyypin. Tällöin olioiden prototyyppeihin tehdyt muutokset heijastuvat siis molempiin. Usein tämä ei ole itse tarkoitus. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Aseta Barin prototyypin uuteen Foo-olioon + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Terve maailma'; + + // Huolehdi siitä, että Bar on todellinen konstruktori + Bar.prototype.constructor = Bar; + + var test = new Bar() // luo uusi bar + + // Prototyyppiketju + test [Bar-olio] + Bar.prototype [Foo-olio] + { foo: 'Terve maailma', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* jne. */ } + +Yllä olio `test` perii sekä `Bar.prototype`- että `Foo.prototype`-olion. Tällöin se pääsee käsiksi `Foo`:ssa määriteltyy funktioon `method`. Se pääsee käsiksi myös ominaisuuteen `value`, jonka luotu `Foo`-olio sisältää prototyypissään. On tärkeää huomata, että `new Bar()` **ei** luo uutta `Foo`-oliota vaan käyttää uudelleen sen prototyyppiin asetettua. Tässä tapauksessa kaikki `Bar`-oliot jakavat siis **saman** `value`-ominaisuuden. + +> **Huomio:** **Älä** käytä `Bar.prototype = Foo`-notaatiota. Tässä tapauksessa se ei osoita `Foo`n prototyyppiin vaan funktio-olioon `Foo`. Tällöin prototyyppiketju osoittaa itse asiassa `Function.prototype`-olioon eikä `Foo.prototype`-olioon, kuten oli tarkoitus. `method` ei siis tällöin olisi mukana prototyyppiketjussa. + +### Ominaisuushaut + +Kun olion ominaisuuksien arvoa haetaan, JavaScript käy prototyyppiketjua läpi **ylöspäin**, kunnes se löytää ominaisuuden nimeä vastaavan arvon. + +Jos se saavuttaa ketjun huipun - `Object.prototype`-olion - eikä ole vieläkään löytänyt haettua ominaisuutta, se palauttaa [undefined](#core.undefined) arvon sen sijaan. + +### Prototyyppi-ominaisuus + +Vaikka Prototyyppi-ominaisuutta käytetään prototyyppiketjujen rakentamiseen, voidaan siihen asettaa **mikä tahansa** arvo. Mikäli arvo on primitiivi, se yksinkertaisesti jätetään huomiotta. + + function Foo() {} + Foo.prototype = 1; // ei vaikutusta + +Kuten esimerkissä yllä, prototyyppiin on mahdollista asettaa olioita. Tällä tavoin prototyyppiketjuja voidaan koostaa dynaamisesti. + +### Suorituskyky + +Prototyyppiketjussa korkealla olevien ominaisuuksien hakeminen voi hidastaa koodin kriittisiä osia. Tämän lisäksi olemattomien ominaisuuksien hakeminen käy koko ketjun läpi. + +Ominaisuuksia [iteroidessa](#object.forinloop) prototyyppiketjun **jokainen** ominaisuus käydään läpi. + +### Natiivien prototyyppien laajentaminen + +JavaScript mahdollistaa `Object.prototype`-olion sekä muiden natiivityyppien laajentamisen. + +Tätä tekniikkaa kutsutaan nimellä [apinapätsäämiseksi][1]. Se rikkoo *kapseloinnin. Vaikka yleisesti käytetyt alustat, kuten [Prototype][2], käyttävätkin sitä, ei ole olemassa yhtään hyvää syytä, minkä takia natiivityyppejä tulisi laajentaa *epästandardilla* toiminnallisuudella. + +**Ainut** hyvä syy on uudempien JavaScript-tulkkien sisältämien ominaisuuksien siirtäminen vanhemmille alustoille. Eräs esimerkki tästä on [`Array.forEach`][3]. + +### Yhteenveto + +Ennen kuin kirjoitat monimutkaista prototyyppiperintää hyödyntävää koodia, on **olennaista**, että ymmärrät täysin kuinka se toimii. Ota huomioon myös prototyyppiketjujen pituus ja riko niitä tarpeen mukaan välttääksesi suorituskykyongelmia. Huomioi myös, että natiiveja prototyyppejä ei tule laajentaa **milloinkaan** ellei kyse ole vain yhteensopivuudesta uudempien JavaScript-ominaisuuksien kanssa. + +[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/external/JavaScript-Garden/doc/fi/other/timeouts.md b/external/JavaScript-Garden/doc/fi/other/timeouts.md new file mode 100644 index 0000000..fd6cc8f --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/other/timeouts.md @@ -0,0 +1,114 @@ +### `setTimeout` ja `setInterval` + +Koska JavaScript on luonteeltaan asynkroninen, voidaan funktioiden suoritusta ajastaa käyttäen `setTimeout` sekä `setInterval`-funktioita. + +> **Huomio:** Aikakatkaisufunktiot **eivät** ole osa ECMAScript-standardia. Ne on toteutettu osana [DOM][1]ia. + + function foo() {} + var id = setTimeout(foo, 1000); // palauttaa Numeron > 0 + +Kun `setTimeout`-funktiota kutsutaan, se palauttaa aikakatkaisun tunnisteen ja ajastaa `foo`-funktion suoritettavaksi **suunnilleen** tuhannen millisekunnin päästä. `foo` suoritetaan tarkalleen **kerran**. + +Käytössä olevan JavaScript-tulkin ajastimen tarkkuudesta, JavaScriptin yksisäikeisyydestä sekä muusta koodista riippuen ei ole **lainkaan** taattua, että viive on tarkalleen sama kuin määritelty. + +Ensimmäisenä annettu funktio suoritetaan *globaalisti*. Tämä tarkoittaa sitä, että sen [`this`](#function.this) on asetettu osoittamaan globaaliin olioon. + + function Foo() { + this.value = 42; + this.method = function() { + // this viittaa globaaliin olioon + console.log(this.value); // tulostaa undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **Huomio:** Koska `setTimeout` ottaa **funktio-olion** ensimmäisenä parametrinaan, usein sitä kutsutaan seuraavasti: `setTimeout(foo(), 1000)`. Tässä tapauksessa se käyttää `foo`:n **palauttamaa arvoa** `foo`:n sijaan. Tämä on hiljainen virhe, koska jos funktio palauttaa arvon `undefined`, `setTimeout` *ei* palauta virhettä. + +### Kutsujen pinoaminen `setInterval`-funktion avulla + +`setTimeout` suoritetaan vain kerran. `setInterval` sen sijaan, kuten nimestä voi päätellä, suoritetaan **aina** `X` millisekunnin välein. Sen käyttöä ei kuitenkaan suositella. + +Mikäli suoritettava koodi blokkaa katkaisufunktion kutsun, `setInterval` lisää kutsuja pinoon. Tämä voi olla ongelmallista erityisesti, mikäli käytetään pieniä intervalliarvoja. + + function foo(){ + // jotain joka blokkaa sekunnin ajaksi + } + setInterval(foo, 100); + +Yllä olevassa koodissa `foo`-funktiota kutsutaan, jonka jälleen se blokkaa sekunnin ajan. + +Tämän ajan aikana `setInterval` kasvattaa kutsupinon sisältöä. Kun `foo` on valmis, kutsupinoon on ilmestynyt jo **kymmenen** uutta kutsua suoritettavaksi. + +### Mahdollisesti blokkaavan koodin kanssa pärjääminen + +Helpoin ja joustavin tapa on käyttää `setTimeout`-funktiota funktiossa itsessään. + + function foo(){ + // jotain joka blokkaa sekunnin ajaksi + setTimeout(foo, 100); + } + foo(); + +Sen lisäksi että tämä ratkaisu kapseloi `setTimeout`-kutsun, se myös estää kutsujen pinoutumisen ja tarjoaa joustavuutta. `foo` voi päättää halutaanko se suorittaa uudelleen vai ei. + +### Katkaisujen poistaminen käsin + +Katkaisuja ja intervalleja voidaan poistaa antamalla sopiva tunniste joko `clearTimeout`- tai `clearInterval`-funktiolle. Se kumpaa käytetään riippuu käytetystä `set`-funktiosta. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Kaikkien katkaisujen poistaminen + +JavaScript ei sisällä erityistä funktiota kaikkien katkaisujen ja/tai intervallien poistamiseen. Sen sijaan tämä voidaan toteuttaa raakaa voimaa käyttäen. + + // poista "kaikki" katkaisut + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +On mahdollista, että jopa tämän jälkeen on olemassa katkaisuja, jotka ovat käynnissä. Onkin siis suositeltavaa tallentaa katkaisujen tunnisteet jotenkin. Tällä tavoin ne voidaan poistaa käsin. + +### Piilotettu `eval` + +`setTimeout` ja `setInterval` voivat ottaa myös merkkijonon ensimmäisenä parametrinaan. Tätä ominaisuutta ei tule käyttää **ikinä**, koska se käyttää sisäisesti `eval`-funktiota. + +> **Huomio:** Koska ECMAScript-standardi **ei** määrittele, kuinka katkaisujen tulee toimia, tapa jolla ne toimivat tässä tapauksessa voi vaihdella JavaScript-toteutuksesta riippuen. Esimerkiksi Microsoftin JScript käyttää `Function`-konstruktoria `eval`-funktion sijaan. + + function foo() { + // kutsutaan + } + + function bar() { + function foo() { + // ei kutsuta ikinä + } + setTimeout('foo()', 1000); + } + bar(); + +Koska `eval`-funktiota ei kutsuta [suoraan](#core.eval), `setTimeout`-funktiolle annettu merkkijono suoritetaan *globaalissa näkyvyysalueessa*. Tässä tapauksessa se ei siis käytä paikallista `bar`-funktion näkyvyysalueessa olevaa `foo`-funktiota. + +Tämän lisäksi on suositeltavaa olla **käyttämättä** merkkijonoja parametrien antamiseen. + + function foo(a, b, c) {} + + // Älä käytä tätä IKINÄ + setTimeout('foo(1,2, 3)', 1000) + + // Käytä nimetöntä funktiota sen sijaan + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Huomio:** Vaikka syntaksi `setTimeout(foo, 1000, 1, 2, 3)` onkin mahdollinen, ei sen käyttöä suositella. Tämä johtuu siitä, että sen käyttö voi johtaa virheisiin erityisesti [metodien](#function.this) kanssa. + +### Yhteenveto + +Merkkijonoa ei tule antaa `setTimeout`- tai `setInterval`-funktiolle **koskaan**. Tämä on selvä merkki **erittäin** huonosta koodista erityisesti mikäli sitä käytetään parametrien välittämiseen. Sen sijaan kannattaa käyttää *nimetöntä funktiota*, joka huolehtii varsinaisesta kutsusta. + +Tämän lisäksi `setInterval`-funktion käyttöä tulee välttää. Tämä johtuu siitä, että sen JavaScript ei blokkaa sen vuorottajaa. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/fi/types/casting.md b/external/JavaScript-Garden/doc/fi/types/casting.md new file mode 100644 index 0000000..6460bf3 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/types/casting.md @@ -0,0 +1,61 @@ +## Tyyppimuunnokset + +JavaScript on tyypitetty *heikosti*. Tämä tarkoittaa sitä, että se pyrkii *pakottamaan tyyppejä* *aina* kun se on mahdollista. + + // Nämä ovat totta + new Number(10) == 10; // Number.toString() muutetaan + // takaisin numeroksi + + 10 == '10'; // Merkkijonot muutetaan Number-tyyppiin + 10 == '+10 '; // Lisää merkkijonohauskuutta + 10 == '010'; // Ja lisää + isNaN(null) == false; // null muuttuu nollaksi, + // joka ei ole NaN + + // Nämä ovat epätosia + 10 == 010; + 10 == '-10'; + +> **ES5 Huomio:** Nollalla alkavat numeroliteraalit tulkitaan oktaaleina (kantaluku 8). Tuki oktaaleille on **poistettu** ECMAScript 5:den tiukassa moodissa. + +Yllä havaittu käytös voidaan välttää käyttämällä [tiukkaa vertailuoperaattoria](#types.equality). Sen käyttöä suositellaan **lämpimästi**. Vaikka se välttääkin useita yleisiä ongelma, sisältää se omat ongelmansa, jotka johtavat juurensa JavaScriptin heikkoon tyypitykseen. + +### Natiivien tyyppien konstruktorit + +Natiivien tyyppien, kuten `Number` tai `String`, konstruktorit käyttäytyvät eri tavoin `new`-avainsanan kanssa ja ilman. + + new Number(10) === 10; // Epätosi, Object ja Number + Number(10) === 10; // Tosi, Number ja Number + new Number(10) + 0 === 10; // Tosi, johtuu tyyppimuunnoksesta + +`Number`-tyypin kaltaisen natiivityypin käyttäminen luo uuden `Number`-olion. `new`-avainsanan pois jättäminen tekee `Number`-funktiosta pikemminkin muuntimen. + +Tämän lisäksi literaalit tai ei-oliomaiset arvot johtavat edelleen uusiin tyyppimuunnoksiin. + +Paras tapa suorittaa tyyppimuunnoksia on tehdä niitä **selvästi**. + +### Muunnos merkkijonoksi + + '' + 10 === '10'; // tosi + +Arvo voidaan muuttaa merkkijonoksi helposti lisäämällä sen eteen tyhjä merkkijono. + +### Muunnos numeroksi + + +'10' === 10; // tosi + +**Unaarinen** plus-operaattori mahdollistaa numeroksi muuttamisen. + +### Muunnos totuusarvoksi + +Arvo voidaan muuttaa totuusarvoksi käyttämällä **not**-operaattoria kahdesti. + + !!'foo'; // tosi + !!''; // epätosi + !!'0'; // tosi + !!'1'; // tosi + !!'-1' // tosi + !!{}; // tosi + !!true; // tosi + + diff --git a/external/JavaScript-Garden/doc/fi/types/equality.md b/external/JavaScript-Garden/doc/fi/types/equality.md new file mode 100644 index 0000000..92c70b7 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/types/equality.md @@ -0,0 +1,58 @@ +## Yhtäsuuruus ja vertailut + +JavaScript sisältää kaksi erilaista tapaa, joiden avulla olioiden arvoa voidaan verrata toisiinsa. + +### Yhtäsuuruusoperaattori + +Yhtäsuuruusoperaattori koostuu kahdesta yhtäsuuruusmerkistä: `==` + +JavaScript tyypittyy *heikosti*. Tämä tarkoittaa sitä, että yhtäsuuruusoperaattori **muuttaa** tyyppejä verratakseen niitä keskenään. + + "" == "0" // epätosi + 0 == "" // tosi + 0 == "0" // tosi + false == "false" // epätosi + false == "0" // tosi + false == undefined // epätosi + false == null // epätosi + null == undefined // tosi + " \t\r\n" == 0 // tosi + +Yllä oleva taulukko näyttää tyyppimuunnoksen tulokset. Tämä onkin eräs pääsyistä, minkä vuoksi `==`-operaattorin käyttöä pidetään huonona asiana. Sen käyttö johtaa hankalasti löydettäviin bugeihin monimutkaisista muunnossäännöistä johtuen. + +Tämän lisäksi tyyppimuunnos vaikuttaa suorituskykyyn. Esimerkiksi merkkijono tulee muuttaa numeroksi ennen kuin sitä voidaan verrata toiseen numeroon. + +### Tiukka yhtäsuuruusoperaattori + +Tiukka yhtäsuuruusoperaattori koostuu **kolmesta** yhtäsuuruusmerkistä: `===` + +Se toimii aivan kuten normaali yhtäsuuruusoperaattori. Se **ei** tosin tee minkäänlaista tyyppimuunnosta ennen vertailua. + + "" === "0" // epätosi + 0 === "" // epätosi + 0 === "0" // epätosi + false === "false" // epätosi + false === "0" // epätosi + false === undefined // epätosi + false === null // epätosi + null === undefined // epätosi + " \t\r\n" === 0 // epätosi + +Yllä olevat tulokset ovat huomattavasti selkeämpiä ja mahdollistavat koodin menemisen rikki ajoissa. Tämä kovettaa koodia ja tarjoaa myös parempaa suorituskykyä siinä tapauksessa, että operandit ovat erityyppisiä. + +### Olioiden vertailu + +Vaikka sekä `==` ja `===` ovat **yhtäsuuruusoperaattoreita**, ne toimivat eri tavoin, kun ainakin yksi operandeista sattuu olemaan `Object`. + + {} === {}; // epätosi + new String('foo') === 'foo'; // epätosi + new Number(10) === 10; // epätosi + var foo = {}; + foo === foo; // tosi + +Tässä tapauksessa molemmat operaattorit vertaavat olion **identiteettiä** **eikä** sen arvoa. Tämä tarkoittaa sitä, että vertailu tehdään olion **instanssin** tasolla aivan, kuten Pythonin `is`-operaattorin tai C:n osoitinvertailun tapauksessa. + +### Yhteenveto + +On erittäin suositeltavaa, että ainoastaan **tiukkaa yhtäsuuruusoperaattoria** käytetään. Mikäli tyyppejä tulee muuttaa, tämä kannattaa tehdä [selvästi](#types.casting) sen sijaan että luottaisi kielen monimutkaisiin muunnossääntöihin. + diff --git a/external/JavaScript-Garden/doc/fi/types/instanceof.md b/external/JavaScript-Garden/doc/fi/types/instanceof.md new file mode 100644 index 0000000..99d4432 --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/types/instanceof.md @@ -0,0 +1,33 @@ +## `instanceof`-operaattori + +`instanceof`-operaattori vertaa kahden operandinsa konstruktoreita keskenään. Se on hyödyllinen ainoastaan, kun vertaillaan itsetehtyjä olioita. Natiivien tyyppien tapauksessa se on lähes yhtä hyödytön kuin [typeof-operaattori](#types.typeof). + +### Itsetehtyjen olioiden vertailu + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // tosi + new Bar() instanceof Foo; // tosi + + // Tämä asettaa vain Bar.prototype-ominaisuudeksi + // funktio-olion Foo + // Se ei kuitenkaan ole Foon todellinen instanssi + Bar.prototype = Foo; + new Bar() instanceof Foo; // epätosi + +### `instanceof` ja natiivit tyypit + + new String('foo') instanceof String; // tosi + new String('foo') instanceof Object; // tosi + + 'foo' instanceof String; // epätosi + 'foo' instanceof Object; // epätosi + +On tärkeää huomata, että `instanceof` ei toimi olioilla, jotka tulevat muista JavaScript-konteksteista (esim. selaimen eri dokumenteista). Tässä tapauksessa niiden konstruktorit viittaavat eri olioon. + +### Yhteenveto + +`instanceof`-operaattoria tulee käyttää **ainoastaan**, mikäli käsitellään itsetehtyjä olioita saman JavaScript-kontekstin sisällä. Kuten [`typeof`](#types.typeof)-operaattorikin, myös muita sen käyttöjä tulee **välttää**. + diff --git a/external/JavaScript-Garden/doc/fi/types/typeof.md b/external/JavaScript-Garden/doc/fi/types/typeof.md new file mode 100644 index 0000000..4ea28ff --- /dev/null +++ b/external/JavaScript-Garden/doc/fi/types/typeof.md @@ -0,0 +1,64 @@ +## `typeof`-operaattori + +`typeof`-operaattori, kuten myös [`instanceof`](#types.instanceof), on kenties JavaScriptin suurin suunnitteluvirhe. Tämä johtuu siitä, että nämä ominaisuudet ovat liki kokonaan käyttökelvottomia. + +Vaikka `instanceof`-operaattorilla onkin tiettyjä rajattuja käyttötarkoituksia, `typeof`-operaattorille on olemassa vain yksi käytännöllinen käyttötapaus, joka **ei** tapahdu olion tyyppiä tarkasteltaessa. + +> **Huomio:** Vaikka `typeof`-operaattoria voidaankin kutsua funktiomaisesti (`typeof(obj)`), ei tämä ole todellinen funktiokutsu. Sulut käyttäytyvät normaalisti ja niiden palauttamaa arvoa käytetään `typeof`-operaattorin operandina. `typeof`-funktiota **ei** ole olemassa. + +### JavaScriptin tyyppitaulukko + + Arvo Luokka Tyyppi + ------------------------------------- + "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 (Nitro/V8-funktio) + new RegExp("meow") RegExp object (Nitro/V8-funktio) + {} Object object + new Object() Object object + +Yllä olevassa taulukossa *Tyyppi* viittaa arvoon, jonka `typeof`-operaattori palauttaa. Kuten voidaan havaita, tämä arvo voi olla varsin ristiriitainen. + +*Luokka* viittaa olion sisäisen `[[Luokka]]`-ominaisuuden arvoon. + +> **Määritelmää lainaten:** `[[Luokka]]`-arvon tulee olla jokin seuraavista merkkijonoista. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +Jotta kyseiseen arvoon päästään käsiksi, tulee soveltaa `Object.prototype`-ominaisuuden `toString`-metodia. + +### Olion luokka + +Määritelmä antaa tarkalleen yhden keinon, jonka avulla `[[Luokka]]` arvoon voidaan päästä käsiksi. Tämä on mahdollista `Object.prototype.toString`-metodia käyttäen. + + function is(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; + } + + is('String', 'test'); // tosi + is('String', new String('test')); // tosi + +Yllä olevassa esimerkissä `Object.prototype.toString`-metodia kutsutaan arvolla [this](#function.this), jonka arvo on asetettu olion `[[Luokka]]` arvoon. + +> **ES5 Huomio:** Käytännöllisyyden vuoksi `Object.prototype.toString` palautusarvo **muutettiin** `Object`-arvosta `Null`- ja `Undefined`-arvoiksi ECMAScript 5:ssä. + +### Määrittelemättömien muuttujien testaaminen + + typeof foo !== 'undefined' + +Yllä oleva testi kertoo onko `foo` määritelty. Pelkästään siihen viittaaminen palauttaisi `ReferenceError`-virheen. Tämä on ainut asia, johon `typeof`-operaattoria kannattaa käyttää. + +### Yhteenveto + +Ainut tapa, jonka avulla olion tyyppi voidaan tarkistaa luotettavasti, on `Object.prototype.toString`-metodin käyttö, kuten yllä. Kuten yllä oleva tyyppitaulu näyttää, osa `typeof`-operaattorin palautusarvoista on huonosti määritelty. Tästä johtuen ne voivat erota toteutuksesta riippuen. + +Muuttujan määrittelemättömyyden testaaminen on ainut tapaus, jossa `typeof`-operaattoria kannattaa käyttää. Muutoin sen käyttöä kannattaa välttää **hinnalla milla hyvänsä**. + diff --git a/external/JavaScript-Garden/doc/fr/array/constructor.md b/external/JavaScript-Garden/doc/fr/array/constructor.md new file mode 100644 index 0000000..76f9eda --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/array/constructor.md @@ -0,0 +1,27 @@ +## Le constructeur `Array` + +Le constructeur `Array` traite ses paramètres de façon ambigu. +Il est fortement recommandé d'utiliser le littéral de tableau - notation `[]` - pour créer de nouveaux tableaux. + + [1, 2, 3]; // Résultat: [1, 2, 3] + new Array(1, 2, 3); // Résultat: [1, 2, 3] + + [3]; // Résultat: [3] + new Array(3); // Résultat: [] + new Array('3') // Résultat: ['3'] + +Dans les cas où il n'y a qu'un seul argument passé au constructeur `Array`, et quand cet argument est un nombre `Number`, le constructeur va retourner un nouveau tableau *clairsemé* avec la propriété `length` (longueur) fixée à la valeur de l'argument. +Il faut noter que de cette façon, **seulement** la propriété `length` du nouveau tableau sera mise en place, les indices réels du tableau ne seront pas initialisés. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // faux, l'indice n'existe pas + +Être en mesure de régler la longueur du tableau à l'avance n'est utile que dans quelques cas, comme la répétition d'une chaîne de caractères, dans lequel on évite l'utilisation d'une boucle. + + new Array(count + 1).join(chaineARepeter); + +### En conclusion + +Les littéraux sont préférés au constructeur `Array`. Ils sont plus courts, ont une syntaxe plus claire, et augmente la lisibilité du code. + diff --git a/external/JavaScript-Garden/doc/fr/array/general.md b/external/JavaScript-Garden/doc/fr/array/general.md new file mode 100644 index 0000000..aecec79 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/array/general.md @@ -0,0 +1,46 @@ +## Tableaux: iteration et propriétés + +Bien que les tableaux soient des objets en JavaScript, il n'y a pas de bonnes raisons d'utiliser la boucle [`for in`](#object.forinloop). +En fait, il y a un certain nombre de bonnes raisons **contre** l'utilisation de `for in` sur les tableaux. + +**Remarque:** Les tableaux JavaScript ne sont **pas** *associatifs*. JavaScript n'offre que les [objets](#object.general) pour associer des clés à des valeurs. Contrairement aux tableaux associatifs, les objets ne préservent **pas** l'ordre. + +La boucle `for in` énumère toutes les propriétés qui sont sur la chaîne de prototypes, et le seul moyen d'exclure ces propriétés consiste à utiliser +[`hasOwnProperty`](#object.hasownproperty), par conséquent la boucle `for in ` est **vingt fois** plus lente qu'une boucle `for` classique. + +### Itération + +Pour itérer sur les tableaux de façon performante, il est préférable d'utiliser la boucle `for` classique. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Notez l'optimization supplémentaire dans l'exemple ci-dessus: la longueur du tableau est mise en mémoire "cached" via `l = list.length`. + +La propriété `length` est définie sur le tableau lui-même, mais la rechercher à chaque itération de la boucle à un coût. +Bien que les moteurs JavaScript récents **peuvent** appliquer l'optimisation, il n'y a aucun moyen de savoir si le code s'exécutera sur un de ces nouveaux moteurs. + +En effet, mettre la longueur du tableau en mémoire cache peut **doubler** la vitesse d'execution de la boucle. + +### La propriété `length` + +Le *getter* de la propriété `length` (longueur) renvoie simplement le nombre d'éléments contenus dans le tableau, mais le *setter* peut être utilisé pour +tronquer le tableau. + + var arr = [1, 2, 3, 4, 5, 6]; + arr.length = 3; + arr; // [1, 2, 3] + + arr.length = 6; + arr.push(4); + arr; // [1, 2, 3, undefined, undefined, undefined, 4] + +Attribuer une longueur inférieure tronque le tableau. Accroître la longueur crée un tableau clairsemé. + +### En conclusion + +Pour de meilleures performances, il est recommandé de toujours utiliser la boucle `for` classique et de mettre en mémoire la propriété `length`. +L'utilisation de la boucle `for in` sur un tableau est un signe de code mal écrit, de mauvaise performance, et sujet à des bogues. + diff --git a/external/JavaScript-Garden/doc/fr/core/delete.md b/external/JavaScript-Garden/doc/fr/core/delete.md new file mode 100644 index 0000000..5ef0b16 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/core/delete.md @@ -0,0 +1,76 @@ +## L'opérateur `delete` + +Il est *impossible* de supprimer les variables globales, fonctions et autres choses qui ont l'attribut `DontDelete` en JavaScript. + +### Le code global et le code de fonction + +Quand une variable ou une fonction est définie dans la portée globale ou une [portée de fonction](#function.scopes), c'est une propriété soit de l'objet d'activation, soit de l'objet global. +Ces propriétés ont un ensemble d'attributs, dont l'un est `DontDelete`. Les déclarations de variables et de fonctions dans le code global et le code de fonction vont toujours créer des propriétés avec `DontDelete`, elle ne peuvent donc pas être supprimées. + + // global variable: + var a = 1; // DontDelete est mis + delete a; // faux + a; // 1 + + // normal function: + function f() {} // DontDelete is mis + delete f; // faux + typeof f; // "function" + + // reassigner n'aide pas: + f = 1; + delete f; // faux + f; // 1 + +### Propriétés explicites + +Les propriétés crées explicitement peuvent être supprimées normalement. + + // propriété crée explicitement: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // vrai + delete obj.y; // vrai + obj.x; // undefined + obj.y; // undefined + +Dans l'exemple ci-dessus, les propriétés `obj.x` et `obj.y` peuvent être supprimées parce qu'elles n'ont pas l'attribut `DontDelete`. C'est aussi pourquoi l'exemple ci-dessous fonctionne également. + + // ceci fonctionne, sauf sur IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // vrai - juste une var globale + delete GLOBAL_OBJECT.a; // vrai + GLOBAL_OBJECT.a; // undefined + +Ici, nous utilisons une astuce pour supprimer `a`. [`this`](#function.this) se réfère ici à l'objet global et nous déclarons explicitement la variable `a` comme sa propriété, ce qui nous permet de la supprimer. + +IE (au moins 6-8) a quelques bogues, le code ci-dessus n'y fonctionne pas. + +### Les arguments de fonction et built-ins + +Les arguments normaux de fonctions, [objets `arguments`](#Function.arguments) et les propriétés intégrées "built-in" ont aussi l'attribut `DontDelete`. + + // les arguments de fonction et les propriétés: + (function (x) { + + delete arguments; // faux + typeof arguments; // "object" + + delete x; // faux + x; // 1 + + function f(){} + delete f.length; // faux + typeof f.length; // "number" + + })(1); + +### Objets hôtes + +Le comportement de l'opérateur `delete` peut être imprévisible pour les objets hébergés "hosted". Dû à la spécification, les objets hôte sont autorisés à mettre en œuvre tout type de comportement. + +### En conclusion + +L'opérateur `delete` a souvent un comportement inattendu et ne peut être utilisé que pour supprimer les propriétés explicitement définies sur des objets normaux. + diff --git a/external/JavaScript-Garden/doc/fr/core/eval.md b/external/JavaScript-Garden/doc/fr/core/eval.md new file mode 100644 index 0000000..5b2cdcd --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/core/eval.md @@ -0,0 +1,42 @@ +## Il ne faut pas utiliser `eval` + +La fonction `eval` exécute une chaîne de caractères représentant du code JavaScript dans la portée locale. + + var number = 1; + function test() { + var number = 2; + eval('number = 3'); + return number; + } + test(); // 3 + number; // 1 + +Cependant, `eval` n'exécute dans la portée locale que quand il est appelé directement *et* quand le nom de la fonction appelée est en fait `eval`. + + var number = 1; + function test() { + var number = 2; + var copyOfEval = eval; + copyOfEval('number = 3'); + return number; + } + test(); // 2 + number; // 3 + +L'utilisation de la fonction `eval` doit être évitée. 99,9% de ses "cas d'utilisation" peuvent être obtenues **sans** elle. + +### `eval` déguisé + +Les [fonctions timeout](#other.timeouts) `setTimeout` et `setInterval` acceptent une chaîne comme premier argument. +Cette chaîne sera **toujours** exécutée dans la portée globale car dans ce cas, `eval` n'est pas appelé directement. + +### Problèmes de sécurité + +`eval` est aussi un problème de sécurité, car il exécute **n'importe quel** code qu'on lui donne. +Il devrait **jamais** être utilisé avec des chaînes d'origines inconnues ou douteuses. + +### En conclusion + +`eval` ne devrait jamais être utilisé. Sa presence met en doute le fonctionnement, la performance, et la sécurité du code qui l'utilise. +Si quelque chose a besoin d'`eval` pour pouvoir fonctionner, il ne doit **pas** être utilisé en premier lieu. Un *meilleur design* qui n'utilise pas `eval` doit être trouvé et implementé. + diff --git a/external/JavaScript-Garden/doc/fr/core/semicolon.md b/external/JavaScript-Garden/doc/fr/core/semicolon.md new file mode 100644 index 0000000..75e4ac1 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/core/semicolon.md @@ -0,0 +1,100 @@ +## Insertion automatique du point-virgule + +Bien que JavaScript a une syntaxe de style C, il n'impose **pas** les points-virgules dans le code source. Il est donc possible de les omettre. + +JavaScript n'est pas un langage sans points-virgules. En fait, les points-virgules sont necessaires pour comprendre le code source. Par conséquent, l'analyseur JavaScript les insère **automatiquement** chaque fois qu'il rencontre une erreur d'analyse due à un point-virgule manquant. + + var foo = function() { + } // erreur d'analyse, point-virgule attendu + test() + +L'analyseur insère un point-virgule, puis tente à nouveau. + + var foo = function() { + }; // plus d'error, l'analyse continue + test() + +L'insertion automatique du point-virgule est considérée comme l'un des **plus gros** défauts de conception dans le langage parce que cela *peut* changer le comportement du code. + +### Comment cela marche + +Le code ci-dessous n'a pas de points-virgules, l'analyseur va donc décider où les insérer. + + (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) + +Voici le résultat du jeu de devinette de l'analyseur. + + (function(window, undefined) { + function test(options) { + + // pas inséré, les lignes ont fusionné + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inséré + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inséré + + return; // <- inséré, casse la déclaration return + { // traité comme un bloc + + // un label et une déclaration d'expression + foo: function() {} + }; // <- inséré + } + window.test = test; // <- inséré + + // les lignes ont fusionné ici encore + })(window)(function(window) { + window.someLibrary = {}; // <- inséré + + })(window); //<- inséré + +> **Remarque:** L'analyseur JavaScript ne manipule pas "correctement" les déclarations return suivies par une nouvelle ligne. + +L'analyseur a radicalement changé le comportement du code ci-dessus. Dans certains cas, il fait la **mauvaise chose**. + +### Parenthèse en tête + +En cas de parenthèse en tête, l'analyseur ne va **pas** insérer de point-virgule. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Ce code fusionne en une ligne. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Il y a de **très** fortes chances que `log` ne retourne **pas** de fonction; par conséquent, le programme ci-dessus va produire une erreur de type `TypeError` indiquant que undefined n'est pas un function `undefined is not a function`. + +### En conclusion + +Il est fortement recommandé de ne **jamais** omettre les points-virgules. Il est également recommandé de garder les accolades sur la même ligne que leurs déclarations correspondantes et de ne jamais les omettre pour les déclaration en une ligne `if` / `else`. Ces mesures vont non seulement améliorer la cohérence du code, mais elles empêcheront également l'analyseur JavaScript de changer le comportement du code. + diff --git a/external/JavaScript-Garden/doc/fr/core/undefined.md b/external/JavaScript-Garden/doc/fr/core/undefined.md new file mode 100644 index 0000000..4736b5d --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/core/undefined.md @@ -0,0 +1,55 @@ +## `undefined` et `null` + +JavaScript a deux valeurs distinctes pour "rien": `null` et `undefined`, `undefined` étant la plus utile. + +### La valeur `undefined` + +`undefined` est un type avec exactement une valeur:` undefined`. + +Le langage définit également une variable globale qui a la valeur `undefined`. Cette variable est aussi appelée `undefined`. Cependant, cette variable n'est ni une constante, ni un mot clé du langage, ce que signifie que sa *valeur* peut être facilement écrasée. + +> **Remarque ES5:** `undefined` dans ECMAScript 5 n'est **plus** inscriptible dans le mode stricte, mais son nom peut toujours être outrepassé, par example par une fonction avec le nom `undefined`. + +Voici quelques exemples de cas où la valeur `undefined` est retournée: + + - Accès à la variable globale (non modifié) `undefined`. + - Accès à une variable déclarée, mais *pas encore* initialisée. + - Retours implicites de fonctions sans déclaration `return`. + - Déclarations `return` vides, qui ne renvoient rien. + - Recherches de propriétés inexistantes. + - Paramètres de fonction qui ne ont pas de valeur explicite passée. + - Tout ce qui a été mis à la valeur de `undefined`. + - Toute expression sous forme de `void(expression)`. + +### Changements à la valeur de `undefined` + +Puisque la variable globale `undefined` contient uniquement une copie de la *valeur* réelle `undefined`, l'attribution d'une nouvelle valeur à la variable ne modifie **pas** la valeur du *type* `undefined`. + +Pourtant, pour pouvoir comparer quelque chose contre la valeur de `undefined`, il est d'abord nécessaire pour récupérer la valeur de `undefined`. + +Afin de protéger le code contre une éventuelle variable `undefined` écrasée, une technique commune utilisée consiste à ajouter un paramètre supplémentaire à une [enveloppe anonyme](#function.scopes) et de lui passer aucun argument. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined dans la portée locale + // réfère bien à la valeur `undefined` + + })('Hello World', 42); + +Une autre façon d'obtenir le même effet est d'utiliser une déclaration à l'intérieur de l'enveloppe. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +La seule différence étant quelques caractères de plus pour écrire "var". + +### Utilisation de `null` + +Alors que `undefined` dans le contexte du langage JavaScript est utilisé dans la plupart des cas dans le d'un *null* traditionnel, le `null` réel (un littéral et un type) est juste un autre type de données. + +`null` est utilisé par JavaScript (comme signaler la fin de la chaîne de prototypes avec `Foo.prototype = null`), mais dans presque tous les cas, il peut être remplacé par `undefined`. + diff --git a/external/JavaScript-Garden/doc/fr/function/arguments.md b/external/JavaScript-Garden/doc/fr/function/arguments.md new file mode 100644 index 0000000..84a09ea --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/arguments.md @@ -0,0 +1,118 @@ +## L'objet `arguments` + +Chaque portée "scope" de fonction en JavaScript peut accéder à la variable spéciale `arguments`. +Cette variable contient une liste de tous les arguments qui ont été passés à la fonction. + +> **Remarque:** Si `arguments` a déjà été définie soit par une déclaration `var` +> à l'intérieur de la fonction ou par un paramètre de fonction, l'objet `arguments` ne sera pas créé. + +L'objet `arguments` n'est **pas** un tableau `Array`. Même s'il a la sémantique d'un tableau - à savoir la propriété `length` (longueur) - il n'hérite pas de +`Array.prototype` mais est en fait un `Object`. + +Pour cette raison, il n'est **pas** possible d'utiliser les méthodes de tableau standards comme `push`, `pop` ou `slice` sur `arguments`. +Bien qu'itérer avec une boucle `for` fonctionne, il est nécessaire de convertir la variable `arguments` en un véritable `Array` pour pouvoir lui appliquer les fonctions de tableau `Array` standards. + +### Conversion à Array + +Le code ci-dessous va retourner un nouveau tableau `Array` contenant tous les éléments de l'objet `arguments`. + + Array.prototype.slice.call(arguments); + +Cette conversion est **lente**, il n'est donc **pas** recommandé de l'utiliser dans des sections de code où la performance est critique. + +### Passage d'arguments + +Voici la méthode recommandée pour passer des arguments d'une fonction à une autre. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // faire qqch ici + } + +Une autre astuce consiste à utiliser à la fois `call` et `apply` pour transformer des méthodes - fonctions qui utilisent la +valeur de `this` ainsi que leurs arguments - en des fonctions normales qui n'utilisent que leurs arguments. + + function Person(first, last) { + this.first = first; + this.last = last; + } + + Person.prototype.fullname = function(joiner, options) { + options = options || { order: "western" }; + var first = options.order === "western" ? this.first : this.last; + var last = options.order === "western" ? this.last : this.first; + return first + (joiner || " ") + last; + }; + + // Créer une version non liée de "fullname", utilisable sur n'importe quel + // objet avec les propriétés 'first' et 'last' passées comme premier + // argument. Cette enveloppe n'aura pas besoin de changer si fullname + // change le nombre ou l'ordre des ses arguments. + Person.fullname = function() { + // résultat: Person.prototype.fullname.call(this, joiner, ..., argN); + return Function.call.apply(Person.prototype.fullname, arguments); + }; + + var grace = new Person("Grace", "Hopper"); + + // 'Grace Hopper' + grace.fullname(); + + // 'Turing, Alan' + Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" }); + + +### Paramètres formels et arguments indexés + +L'objet `arguments` crée des fonctions *getter* et *setter* à la fois pour ses propriétés et les paramètres formels de la fonction. + +Par conséquent, changer la valeur d'un paramètre formel va également modifier la valeur de la propriété correspondante sur l'objet `arguments`, et vice-versa. + + 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); + +### Mythes et faits sur la performance + +Le seul moment où l'objet `arguments` n'est pas créé est quand il est déclaré comme un nom à l'intérieur d'une fonction ou l'un de ses paramètres formels. Le fait qu'il soit utilisé ou non n'est pas important. + +Les deux *getter* et *setter* sont toujours créé; et donc l'utilisation d'`arguments` n'a aucune incidence sur la performance. + +> **Remarque ES5:** Ces *getters* et *setters* ne sont pas créés en mode strict. + +Cependant, un cas va considérablement réduire la performance des moteurs JavaScript modernes. +C'est le cas de l'utilisation de `arguments.callee`. + + function foo() { + arguments.callee; // faire quelque chose avec cet objet de fonction + arguments.callee.caller; // et la fonction appelante + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Seraient normalement inline... + } + } + +Dans le code ci-dessus, `foo` ne peut plus être [inline][1] car il a besoin de se connaitre lui-même et connaitre son appelant. +Cela défait les gains possibles de performance qui découleraient d'inline, mais cela casse également l'encapsulation +car la fonction peut maintenant être dépendante d'un contexte d'appel spécifique. + +Utiliser `arguments.callee` ou l'une de ses propriétés est **fortement déconseillé**. + +> **Remarque ES5:** En mode strict, `arguments.callee` jettera une erreur de type `TypeError` +> car son utilisation est marquée comme obsolète "deprecated". + +[1]: http://en.wikipedia.org/wiki/Inlining + diff --git a/external/JavaScript-Garden/doc/fr/function/closures.md b/external/JavaScript-Garden/doc/fr/function/closures.md new file mode 100644 index 0000000..50983db --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/closures.md @@ -0,0 +1,97 @@ +## Fermetures et réferences + +Les *fermetures* "closures" sont une des fonctionnalités les plus puissantes de JavaScript. +Avec les fermetures, les portées gardent **toujours** l'accès à la portée externe, dans laquelle elles ont été définies. +Puisque la seule portée que JavaScript a est la [portée de fonction](#function.scopes), toutes les fonctions, par défaut, agissent comme des fermetures. + +### Simuler les variables privées + + function Counter(start) { // compteur + var count = start; // compte + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Ici, `Counter` retourne **deux** fermetures: la fonction` increment` ainsi que la fonction `get`. Ces deux fonctions conservent une **référence** à la portée de `Counter` et, par conséquent, gardent toujours l'accès à la variable `count` qui a été définie dans cette portée. + +### Comment marchent les variables privées + +Comme il ne est pas possible de référencer ou assigner des portées en JavaScript, il n'y a **aucun** moyen d'accéder à la variable `count` de l'extérieur. +La seule façon d'interagir avec elle est par l'intermédiaire des deux fermetures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +Le code ci-dessus ne va **pas** changer la variable `count` dans la portée de `Counter`, car `foo.hack` n'a pas été défini dans cette portée. En fait, une nouvelle variable va etre crée - ou va remplacer - la variable *globale* `count`. + +### Fermetures dans les boucles + +Une erreur souvent commise est d'utiliser les fermetures à l'intérieur de boucles comme si elles copiaient la valeur de la variable d'indice de la boucle. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +La programme ci-dessus ne vas **pas** produire les numéros `0` à `9`, il imprimera `10` dix fois. + +La fonction *anonyme* garde une **référence** à `i`. Au moment où `console.log` est appelée, la `boucle for` est déjà achevée, et donc la valeur de `i` est à `10`. + +Afin d'obtenir le comportement souhaité, il est nécessaire de créer une **copie** de la valeur de `i`. + +### Eviter le problème de référence + +Pour copier la valeur de la variable d'index de la boucle, il est préférable d'utiliser une [enveloppe anonyme](#function.scopes) "wrapper". + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +La fonction externe anonyme est appelée immédiatement avec `i` en tant que premier argument, et donc le paramètre `e` recevra une copie de la **valeur** de `i`. + +La fonction anonyme qui est passé à `setTimeout` a maintenant une référence à `e`, dont la valeur ne peut **pas** être changée par la boucle. + +Une autre façon de faire est de retourner une fonction de l'enveloppe anonyme qui aura alors le même comportement que le code ci-dessus. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +Une autre façon populaire d'achever le même comportement est d'ajouter un argument supplémentaire à la fonction `setTimeout`. La fonction passera ces arguments à la fonction de rappel "callback". + + for(var i = 0; i < 10; i++) { + setTimeout(function(e) { + console.log(e); + }, 1000, i); + } + +Sachez que certains environnements JS (Internet Explorer 9 et avant) ne supportent pas cette dernière approche. + +Enfin, une dernière façon de faire et d'utiliser `bind`, qui peut lier le contexte `this` et les arguments pour la fonction. + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } + diff --git a/external/JavaScript-Garden/doc/fr/function/constructors.md b/external/JavaScript-Garden/doc/fr/function/constructors.md new file mode 100644 index 0000000..e92dea9 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/constructors.md @@ -0,0 +1,109 @@ +## Constructeurs + +Les constructeurs en JavaScript diffèrent de beaucoup d'autres langages. +Tout appel de fonction précédé par le mot clé `new` agit comme un constructeur. + +Dans le constructeur - la fonction appelée - la valeur de `this` se réfère à un objet nouvellement créé. +Le [prototype](#Object.prototype) de ce **nouvel** objet pointe sur le `prototype` de l'objet de fonction qui a été invoqué comme constructeur. + +Si la fonction qui a été appelée n'a pas de déclaration `return` explicite, elle renvoira implicitement la valeur de `this` - le nouvel objet. + + function Person(name) { + this.name = name; + } + + Person.prototype.logName = function() { + console.log(this.name); + }; + + var sean = new Person(); + +Le code ci-dessus appelle `Person` en tant que constructeur et définit le `prototype` du nouvel objet créé à `Person.prototype`. + +En cas d'une déclaration `return` explicite, la fonction renvoie la valeur spécifiée par cette déclaration, mais **seulement** si cette valeur est un objet `Object`. + + function Car() { + return 'ford'; + } + new Car(); // un nouvel objet, pas 'ford' + + function Person() { + this.someValue = 2; + + return { + name: 'Charles' + }; + } + new Test(); // l'objet retourné ({name:'Charles'}) n'inclue pas someValue + +Lorsque le mot clé `new` est omis, la fonction ne retournera **pas** un nouvel objet. + + function Pirate() { + this.hasEyePatch = true; // this est l'object global! + } + var somePirate = Pirate(); // somePirate est undefined + +Bien que l'exemple ci-dessus a l'air de marcher, il utilisera l'*objet global* pour la valeur de `this`, en raison du fonctionnement particulier de [`this`](#function.this) en JavaScript. + +### Fabriques + +Pour pouvoir omettre le mot clé `new`, la fonction constructeur doit retourner explicitement une valeur. + + function Robot() { + var color = 'gray'; + return { + getColor: function() { + return color; + } + } + } + Robot.prototype = { + someFunction: function() {} + }; + + new Robot(); + Robot(); + +Les deux appels à `Robot` retournent la même chose, un objet nouvellement créé qui possède une propriété appelée `getColor`, qui est une [fermeture](#function.closures) "closure". + +Il convient également de noter que l'appel `new Robot()` n'affecte **pas** le prototype de l'objet retourné. +Bien que le prototype sera mis sur le nouvel objet créé, `Robot` ne retourne jamais cet objet. + +Dans l'exemple ci-dessus, il n'y a pas de différence fonctionnelle entre l'utilisation et la non-utilisation du mot clé `new`. + +### Creation de nouvels objects via fabriques + +Il est souvent recommandé de ne **pas** utiliser `new` car l'oublier peut conduire à des bugs. + +Pour créer un nouvel objet, il faut plutôt utiliser une fabrique qui va construire un nouvel objet. + + function CarFactory() { + var car = {}; + car.owner = 'nobody'; + + var milesPerGallon = 2; + + car.setOwner = function(newOwner) { + this.owner = newOwner; + } + + car.getMPG = function() { + return milesPerGallon; + } + + return car; + } + +Bien que le code qui précède est robuste contre un mot clé `new` manquant et rend certainement +l'utilisation de [variables privées](#function.closures) plus facile, il y a des inconvénients. + + 1. Il utilise plus de mémoire car les objets créés ne partagent **pas** leurs méthodes avec un prototype. + 2. Pour hériter, la fabrique a besoin de copier toutes les méthodes de l'autre objet ou mettre l'autre objet sur le prototype du nouvel objet. + 3. Abandonner la chaîne de prototype à cause d'un mot clé `new` laissé de côté est contraire à l'esprit du langage. + +### En Conclusion + +Omettre le mot clé `new` peut conduire à des bugs, mais ce n'est certainement **pas** une raison d'abandonner l'utilisation des prototypes. +En fin de compte il s'agit de savoir quelle solution est la mieux adaptée pour les besoins de l'application. +Il est particulièrement important de choisir un style spécifique de création d'objet et toujours l'utiliser afin de rester *cohérent*. + diff --git a/external/JavaScript-Garden/doc/fr/function/general.md b/external/JavaScript-Garden/doc/fr/function/general.md new file mode 100644 index 0000000..b6fe47e --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/general.md @@ -0,0 +1,38 @@ +## Déclaration des fonctions et expressions + +Les fonctions en JavaScript sont des objets de première classe. Cela signifie qu'elles peuvent être passées comme toute autre valeur. Une utilisation courante de cette caractéristique est de passer une *fonction anonyme* comme une fonction de rappel "callback" qui peut être asynchrone. + +### La déclaration `function` + + function foo() {} + +La fonction ci-dessus est [hissée](#function.scopes) "hoisted" avant le démarrage du programme; ainsi, elle est donc disponible partout dans la portée "scope" d'application où la fonction a été définie, même si appelé avant sa définition dans le code source. + + foo(); // Fonctionne car foo a été crée avant l'exécution de ce code + function foo() {} + +### L'expresssion `function` + + var foo = function() {}; + +Cet exemple attribue une fonction *anonyme* et sans nom à la variable `foo`. + + foo; // 'undefined' + foo(); // provoque un erreur de type TypeError + var foo = function() {}; + +En raison du fait que `var` est une déclaration qui hisse le nom de la variable `foo` avant que l'exécution réelle du code ne commence, `foo` est déjà déclarée lorsque le script est exécuté. + +Mais comme les assignements ne se produisent qu'au moment de l'exécution, la valeur de `foo` sera par défaut mise à [undefined](#core.undefined) avant l'exécution du code. + +### L'expression de fonction nommée + +Un autre cas est l'attribution de fonctions nommées. + + var foo = function bar() { + bar(); // Works + } + bar(); // erreur de reference ReferenceError + +Ici, `bar` n'est pas disponible dans la portée externe "outer scope", puisque la fonction est seulement assignée à `foo`, mais elle est disponible à l'intérieur de `bar`. Cela est dû à la méthode de [résolution de noms](#function.scopes) de JavaScript: le nom de la fonction est *toujours* disponible dans la portée locale "local scope" de la fonction elle-même. + diff --git a/external/JavaScript-Garden/doc/fr/function/scopes.md b/external/JavaScript-Garden/doc/fr/function/scopes.md new file mode 100644 index 0000000..7cd1d0b --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/scopes.md @@ -0,0 +1,204 @@ +## Portées "scopes" et espaces de noms "namespaces" + +Bien que JavaScript utilise une syntaxe avec accolades pour les blocs, il ne crée **pas** de portée "scope" de bloc; par conséquent, la seule portée du langage est la portée de fonction. + + function test() { // une portée "scope" + for(var i = 0; i < 10; i++) { // pas une portée + // count + } + console.log(i); // 10 + } + +> **Remarque:** Lorsqu'elle n'est pas utilisé dans un assignement, une déclaration de retour, +> ou un argument de fonction, la notation `{...}` sera interprétée comme une déclaration +> de bloc et non **pas** comme un littéral d'objet. Ceci, quand combiné avec +> l'[insertion automatique des points-virgules](#de core.semicolon), peut conduire à des erreurs subtiles. + +Il n'existe pas d'espaces de noms "namespaces" en JavaScript, ce qui signifie que tout est défini dans un espace de noms commun partagé par tous. + +Chaque fois qu'une variable est référencée, JavaScript va traverser vers le haut toutes les portées jusqu'à ce qu'il la trouve. +S'il atteint la portée globale sans avoir trouvé le nom demandé, il va générer une erreur de référence `ReferenceError`. + +### Le fléau des variables globales + + // script A + foo = '42'; + + // script B + var foo = '42' + +Les deux scripts ci-dessus n'ont **pas** le même effet. Le script A définit une variable appelée `foo` dans la portée *globale*, le script B définit `foo` dans la portée actuelle. + +Ne pas utiliser `var` peut avoir des répercussions majeures. + + // portée globale + var foo = 42; + function test() { + // portée locale + foo = 21; + } + test(); + foo; // 21 + +En laissant de côté la déclaration `var` à l'intérieur de la fonction `test`, on remplace la valeur de `foo`. +Même si au premier abord cela ne semble pas être une grosse affaire, des milliers de lignes de JavaScript qui n'utilisent pas `var` créeront des bogues horribles qui seront très difficiles à dépister. + + // portée globale + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // portée de subLoop + for(i = 0; i < 10; i++) { // var manquant + // ici, des choses incroyables! + } + } + +La boucle externe se terminera après le premier appel à `subLoop`, car `subLoop` écrase la valeur globale de `i`. +L'utilisation d'un `var` pour la deuxième boucle `for` aurait facilement évité cette erreur. +La déclaration de `var` devrait **jamais** être laissé de côté, sauf si l'*effet désiré* est d'affecter la portée externe. + +### Variables locales + +Seuls les paramètres de [fonction](#function.general) et les variables déclarées avec un `var` peuvent créer des variables locales en JavaScript. + + + // portée globale + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // portée locale de la fonction test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` et `i` sont bien des variables locales à l'intérieur de la portée de la fonction `test`, mais l'assignment `bar` remplacera la variable globale portant le même nom. + +### Remontée "hoisting" + +JavaScript **hisse** les déclarations. Cela signifie que les déclarations de `var` et `function` seront déplacés vers le haut de leur portée englobante. + + 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]; + } + } + +Le code ci-dessus est transformé avant que l'exécution ne commence. JavaScript déplace les déclarations `var`, ainsi que les déclarations `function`, vers le haut de la portée la plus proche. + + // les déclarations var sont maintenant ici + var bar, someValue; // mis à 'undefined' par défaut + + // les déclarations de fonction aussi + function test(data) { + var goo, i, e; // pas de portée de bloc, + // donc déclarations var viennent ici + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // échoue avec TypeError puisque bar est toujours 'undefined' + someValue = 42; // les assignements ne sont pas concernés par la remontée + bar = function() {}; + + test(); + +L'inexistence des portées de bloc va non seulement déplacer les déclarations `var` en dehors du corps des boucles, mais va aussi rendre les résultats de certaines constructions de `if` non-intuitifs. + +Dans le code original, la déclaration `if` semblait modifier la *variable globale* `goo`, alors qu'en fait elle modifiait la *variable locale* - après la remontée appliquée. + +Sans la connaissance du concept de *remontée*, on pourrait soupçonner que le code ci-dessous produirait une erreur de référence `ReferenceError`. + + // verifie si SomeImportantThing a bien été initializé + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Le code fonctionne pourtant bien, car la déclaration de `var` est déplacé vers le haut de la *portée globale*. + + var SomeImportantThing; + + // du code peut, ou pas, initializer SomeImportantThing ici + + // soyons en sûr + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Ordre de la résolution de noms + +Toutes les portées en JavaScript, y compris la *portée globale*, ont le nom spécial [`this`](#function.this) défini qui se réfère à l'*objet courant*. + +Les portées de fonction ont aussi le nom [`arguments`](#Function.arguments) défini qui contient les arguments qui ont été transmis à la fonction. + +Par exemple, lorsque vous essayez d'accéder à une variable nommé `foo` l'intérieur de la portée d'une fonction, JavaScript va chercher le nom dans l'ordre suivant: + + 1. Si il y a une déclaration `var foo` var dans la portée courante, l'utiliser. + 2. Si l'un des paramètres de la fonction est nommé `foo`, l'utiliser. + 3. Si la fonction elle-même est appelée `foo`, l'utiliser. + 4. Sinon, accéder à la portée externe suivante, et recommencer à **#1** pour cette portée. + +**Remarque:** Avoir un paramètre appelé `arguments` va **empêcher** la création d'objet par défaut `arguments`. + +### Espaces de noms + +Le fait de n'avoir qu'un seul espace de noms global engendre un risque de conflit de noms de variables, un problème commun en JavaScript. +En JavaScript, ce problème peut facilement être évité grâces aux *enveloppes anonymes*. + + (function() { + // un "espace de nom" autonome + + window.foo = function() { + // une fermeture exposée + }; + + })(); // exécute la fonction immédiatement + +Les fonctions anonymes sont considérées comme des [expressions](#function.general); ainsi elles doivent d'abord être évaluées avant d'être appelées. + + ( // évaluer la fonction à l'intérieur des parenthèses + function() {} + ) // et retourner la fonction object + () // appeler le résultat de l'évaluation + +Il y a d'autres façons d'évaluer et d'appeler directement l'expression de fonction qui, bien que différentes dans la syntaxe, se comportent de la même manière. + + // Autres styles d'invocation directe + !function(){}() + +function(){}() + (function(){}()); + // etc. + +### En conclusion + +Il est recommandé de toujours utiliser une *enveloppe anonyme* pour encapsuler du code dans son propre espace de noms. +Non seulement cela protège des conflits de noms de code, cela permet également une meilleure modularisation des programmes. + +En outre, l'utilisation de variables globales est considéré comme une **mauvaise pratique**. +Leur utilisation indique un code mal écrit, sujet à des erreurs, et difficile à maintenir. + diff --git a/external/JavaScript-Garden/doc/fr/function/this.md b/external/JavaScript-Garden/doc/fr/function/this.md new file mode 100644 index 0000000..aed2301 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/function/this.md @@ -0,0 +1,100 @@ +## Comment marche `this` + +Pour JavaScript, ce que le nom spécial `this` réfère à diffère de la plupart des autres langages de programmation. Il y a exactement **cinq** façons différente de lier la valeur de `this` dans le langage. + +### Le contexte global "global scope" + + this; + +Lorsque vous utilisez `this` dans le contexte global, il va simplement référer à l'objet *global*. + +### Appel de fonction + + foo(); + +Ici, `this` va aussi référer à l'objet *global*. + +> **Remarque ES5:** En mode strict, le cas global **n'existe plus**: `this` aura la valeur `undefined`. + +### Appel de méthode + + test.foo(); + +Dans cet exemple, `this` va référer à `test`. + +### Appel de constructeur + + new foo(); + +Un appel de fonction qui est précédé par le mot clé `new` agit comme un [constructeur](#function.constructors). Dans la fonction, `this` va référer à un `Object` *nouvellement créé*. + +### Assignement direct de `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // table va s'étendre comme ci-dessous + foo.call(bar, 1, 2, 3); // mène à: a = 1, b = 2, c = 3 + +Lorsque vous utilisez les méthodes `call` (appeler) ou `apply` (appliquer) de `Function.prototype`, la valeur de `this` à l'intérieur de la fonction appelée est **directement définie** par le premier argument de l'appel correspondant. + +En conséquence, dans l'exemple ci-dessus le cas d'*appel de méthode* ne s'applique **pas**, et `this` à l'intérieur de `foo` va bien référer à `bar`. + +> **Remarque:** `this` ne peut **pas** être utilisé pour se référer à l'objet à l'intérieur d'un littéral `Object`. +> Donc dans `var obj = {moi: this}`, `moi` ne vas **pas** référer à `obj`, puisque `this` ne reçoit une valeur que dans l'un des cinq cas énumérés. + +### Pièges communs + +Bien que la plupart de ces cas ont du sens, le premier cas peut être considéré comme une autre faute de design du langage, car il n'est **jamais** d'aucune utilité pratique. + + Foo.method = function() { + function test() { + // this réfère à l'objet global + } + test(); + }; + +Une autre erreur souvent commise est que `this` l'intérieur de `test` se réfère à `foo`; ce qui n'est **pas** du tout le cas. + +Pour accéder à `foo` de l'intérieur de `test`, vous pouvez créer une variable locale à intérieur de `method` qui fait référence à `foo`. + + Foo.method = function() { + var self = this; + function test() { + // Utilisez self au lieu de this ici + } + test(); + }; + +`self` est juste une variable normale, couramment utilisée pour référencer un `this` extérieur. Combiné avec des [fermetures](# function.closures) "closures", on peut l'utiliser pour passer les valeurs de `this`. + +À partir d'ECMAScript 5, l'utilisation de la méthode `bind` avec une fonction anonyme mène au même resultat: + + Foo.method = function() { + var test = function() { + // maintenant, this réfère à Foo + }.bind(this); + test(); + }; + +### Assignement de méthodes + +Une autre chose qui ne marche **pas** en JavaScript est l'alias de fonction, ou l'**assignement** d'une méthode à une variable. + + var test = someObject.methodTest; + test(); + +En raison du premier des cinq cas, `test` agit maintenant comme un appel de fonction normal; par conséquent, `this` à l'intérieur de la fonction ne va plus référer à `someObject`. + +Bien que la liaison tardive "late binding" de `this` pouvait sembler comme une mauvaise idée au premier abord, c'est en fait grâce à cela que l'[héritage prototypique](#Object.prototype) fonctionne. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Quand `method` est appelée d'une instance de `bar`, `this` va référer à cette même instance. + diff --git a/external/JavaScript-Garden/doc/fr/index.json b/external/JavaScript-Garden/doc/fr/index.json new file mode 100644 index 0000000..9f1bf37 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/index.json @@ -0,0 +1,69 @@ +{ + "title": "Le Jardin de JavaScript", + "langTitle": "JavaScript Garden en Français", + "description": "Un guide des defauts et excentricités de JavaScript", + "sections": [ + { + "title": "Introduction", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Objets", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Fonctions", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Tableaux", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Types", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Cœur", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Autres", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/fr/intro/index.md b/external/JavaScript-Garden/doc/fr/intro/index.md new file mode 100644 index 0000000..71c6ac6 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/intro/index.md @@ -0,0 +1,36 @@ +## Introduction + +Le **Jardin de JavaScript** est une collection croissante de documentation liée aux aspects les plus excentriques du langage de programmation JavaScript. Il donne des conseils pour éviter les erreurs communes, les bugs subtils, ainsi que les problèmes de performance et de mauvaises pratiques, que les amateurs de JavaScript peuvent rencontrer dans leurs efforts d'apprentissage en profondeur du langage. + +Le Jardin de JavaScript ne cherche pas à vous enseigner JavaScript. Une connaissance préalable du langage est fortement recommandée afin de comprendre les sujets abordés dans ce guide. Veuillez vous référer à l'excellent [guide][1] du Mozilla Developer Network pour apprendre les rudiments du langage JavaScript. + +## Auteurs + +Ce guide est l'œuvre de deux charmants utilisateurs de [Stack Overflow][2]: [Ivo Wetzel][3] (écriture) et [Zhang Yi Jiang][4] (design). + +Actuellement maintenu par [Tim Ruffles](http://truffles.me.uk). + +## Collaborateurs + +- Trop nombreux pour être énumérés ici, [voir la liste des collaborateurs](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + +## Hébergement + +Le Jardin de JavaScript est hébergé sur GitHub, mais [Cramer Développement][7] nous soutient avec un mirroir à [JavaScriptGarden.info][8]. + +## Licence + +Le Jardin de JavaScript est publié sous la [licence MIT][9] et hébergé sur [GitHub][10]. Si vous trouvez des erreurs ou fautes de frappe veuillez s'il vous plaît [déposer une question][11] ou une "pull request" sur le dépôt. Vous pouvez également nous trouver dans la [Salle JavaScript][12] sur Stack Overflow. + +[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/external/JavaScript-Garden/doc/fr/object/forinloop.md b/external/JavaScript-Garden/doc/fr/object/forinloop.md new file mode 100644 index 0000000..afc1a2d --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/object/forinloop.md @@ -0,0 +1,44 @@ +## La boucle `for in` + +Tout comme l'opérateur `in`, la boucle `for in` traverse la chaîne de prototypes lors de l'itération sur les propriétés d'un objet. + +> **Remarque:** La boucle `for in` n'itérera **pas** sur les propriétés qui +> ont leur attribut `enumerable` à `false`; par exemple, la propriété `length` d'un tableau "array". + + // Empoisonnement d'Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // imprime bar et moo + } + +Puisqu'il n'est pas possible de changer le comportement de la boucle `for in`, il est nécessaire de filtrer les propriétés indésirables à l'intérieur du corps de la boucle. Sous ECMAScript 3 et plus, cela se fait en utilisant la méthode [`hasOwnProperty`](#object.hasownproperty) de `Object.prototype`. + +Depuis ECMAScript 5, `Object.defineProperty` peut être utilisé avec `enumerable` mis à faux pour ajouter des propriétés à des objets (y compris `Object`) sans que ces propriétés soient énumérées. Il est raisonnable dans ce cas d'assumer que les propriétés énumérables ont été ajouté pour une raison, ce qui permet d'omettre les appels à `hasOwnProperty` qui réduisent la lisibilité du code. Dans du code de librairie, `hasOwnProperty` devrait toujours être utilisé car des propriétés énumérables pourraient résider sur la chaîne de prototypes sans qu'on le sache. + +> **Remarque:** Puisque la boucle `for in` traverse toujours la chaîne de prototypes complet, elle +> deviendra plus lente avec chaque couche supplémentaire d'héritage ajoutée à un objet. + +### Filtrer avec `hasOwnProperty` + + // le même foo qu'au dessus + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Cette version est la seule version correcte à utiliser avec les anciennes versions d'ECMAScript. +L'utilisation de `hasOwnProperty` nous garantie que seulement `moo` sera imprimé. +Quand `hasOwnProperty` n'est pas utilisé, les prototypes natifs - par exemple `Object.prototype` - qui ont peut-être été étendus, causeront probablement des erreurs. + +Avec les versions plus récentes d'ECMAScript, des propriétés non-dénombrables peuvent être définies avec `Object.defineProperty`, réduisant le risque d'itération sur les propriétés quand `hasOwnProperty` n'est pas utilisé. Néanmoins, il faut faire attention avec l'utilisation de vieilles librairies comme [Prototype][1] qui ne bénéficient pas des nouvelles fonctions d'ECMAScript. Dans ce cadre, écrire des boucles `for in` sans `hasOwnProperty` est garanti de causer des erreurs. + +### En conclusion + +Il est recommandé de **toujours** utiliser `hasOwnProperty` avec ECMAScript 3 ou moins, ou dans du code de librairie. Dans ces environnements, il ne faut jamais assumer que les prototypes natifs n'ont pas été étendus. Depuis ECMAScript 5, `Object.defineProperty` permet de définir les propriétés non-dénombrables et donc permet d'omettre les appels à `hasOwnProperty` dans le code de l'application. + + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/fr/object/general.md b/external/JavaScript-Garden/doc/fr/object/general.md new file mode 100644 index 0000000..afa2ea2 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/object/general.md @@ -0,0 +1,85 @@ +## Utilisation des objets et propriétés + + +En JavaScript, tout agit comme un objet, à part deux exceptions: [`null`](#core.undefined) et [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Une méprise commune est que les littéraux numériques ne peuvent pas être utilisés comme objets, due à une imperfection de l'analyseur de JavaScript qui tente d'analyser la *notation à point* sur un nombre comme une virgule flottante. + + 2.toString(); // erreur de syntaxe SyntaxError + +Des solutions de contournement existent pour forcer les littéraux numériques à agir comme des objets. + + 2..toString(); // le second point est correctement reconnu + 2 .toString(); // notez l'espace à gauche du point + (2).toString(); // 2 est évalué en premier + +### Objets comme type de données + +Les objets en JavaScript peuvent également être utilisés comme [*HashMaps*][1]; essentiellement, des propriétés nommées pointant sur des valeurs. + +En utilisant un littéral d'objet - notation `{}` - il est possible de créer un objet vide. +Ce nouvel objet [hérite](#object.prototype) de `Object.prototype` et ne possède pas de [propriétés propres](#object.hasownproperty) définies. + + var foo = {}; // un nouvel objet vide + + // un nouvel objet avec une propriété 'test' à valeur 12 + var bar = {test: 12}; + +### Accéder aux propriétés + +Les propriétés d'un objet sont accessibles de deux façons, soit par la notation à point, soit par la notation à crochets. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // cela marche + +Les deux notations fonctionnent presque pareil, la seule différence étant que la notation à crochet permet l'écriture des propriétés et l'utilisation des noms de propriété qui autrement mèneraient à une erreur de syntaxe. + +### Supprimer des propriétés + +La seule façon de supprimer une propriété d'un objet est d'utiliser l'opérateur `delete`. +Mettre la propriété à `null` ou `undefined` ne supprime que la *valeur* associée à la propriété, et non pas la *propriété* elle-même. + + 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]); + } + } + +Les résultats du programme ci-dessus sont `bar undefined` et `foo null` - seul `baz` a été correctement supprimé. + +### Notation des clefs "keys" + + var test = { + 'case': 'Je suis un mot-clé, donc je dois etre écrit en tant que chaîne', + delete: 'Je suis un mot-clé, donc moi aussi' // erreur de syntaxe SyntaxError + }; + +Les propriétés d'objet peuvent être écrites simplement telles quelles ou comme des chaînes "string". Une autre imperfection de l'analyseur de JavaScript, avant ECMAScript 5, provoquera une erreur de syntaxe `SyntaxError` dans le programme qui précède. + +Cette erreur vient du fait que `delete` est un *mot-clé*; et par conséquent, il doit être écrit comme une *chaîne* littérale pour s'assurer qu'il sera correctement interprété par les vieux moteurs JavaScript. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/fr/object/hasownproperty.md b/external/JavaScript-Garden/doc/fr/object/hasownproperty.md new file mode 100644 index 0000000..aa91210 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/object/hasownproperty.md @@ -0,0 +1,46 @@ +## `hasOwnProperty` + +Pour savoir si un objet possède une propriété définie, et non pas quelque part ailleurs sur sa [chaîne de prototype](#object.prototype), il est nécessaire d'utiliser la méthode `hasOwnProperty`, une méthode que tous les objets héritent d'`Object.prototype`. + +> **Remarque:** Il n'est **pas** suffisant de vérifier si une propriété est `undefined`, +> car la propriété peut très bien exister, mais avec une valeur `undefined`. + +`hasOwnProperty` est la seule chose en JavaScript qui traite des propriétés **sans** traverser la chaîne de prototypes. + + // Empoisonnement d'Object.prototype + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // vrai + + foo.hasOwnProperty('bar'); // faux + foo.hasOwnProperty('goo'); // vrai + +Seulement `hasOwnProperty` donnera le résultat attendu et correct. Voir la section sur [les boucles `for in`](#object.forinloop) pour plus de détails sur l'utilisation de `hasOwnProperty` pour traverser les propriétés d'un objet. + +### `hasOwnProperty` en tant que propriété + +JavaScript ne protège pas le nom de la propriété `hasOwnProperty`; ainsi, la possibilité existe qu'un objet peut avoir une propriété avec ce nom, et il est donc nécessaire d'utiliser une méthode `hasOwnProperty` *externe* pour obtenir des résultats corrects. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // toujours faux + + // Utiliser hasOwnProperty d'un autre object, + // et l'appeler avec foo assigné à 'this' + ({}).hasOwnProperty.call(foo, 'bar'); // vrai + + // Il est aussi possible d'utiliser hasOwnProperty + //du prototype d'Object + Object.prototype.hasOwnProperty.call(foo, 'bar'); // vrai + +### En conclusion + +Utiliser `hasOwnProperty` est la **seule** méthode fiable pour vérifier l'existence d'une propriété sur un objet. Il est recommandé d'utiliser `hasOwnProperty` pour itérer sur les propriétés des objets comme décrit dans la section sur [les boucles `for in`](#object.forinloop). + diff --git a/external/JavaScript-Garden/doc/fr/object/prototype.md b/external/JavaScript-Garden/doc/fr/object/prototype.md new file mode 100644 index 0000000..c1a471a --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/object/prototype.md @@ -0,0 +1,85 @@ +## Le prototype + +JavaScript n'utilise pas le modèle classique d'héritage, mais un modèle *prototypique*. + +Souvent considéré comme l'une des faiblesses de JavaScript, le modèle d'héritage prototypique est en fait plus puissant que le modèle classique. Par exemple, il est assez facile de construire un modèle classique à partir du modèle prototypique, tandis que l'inverse est une tâche beaucoup plus difficile à entreprendre. + +JavaScript étant le seul langage à héritage prototypique largement utilisé, s'adapter aux différences entre les deux modèles peut prendre du temps. + +La première différence majeure est que l'héritage en JavaScript utilise des *chaînes de prototypes*. + +> **Remarque:** Utiliser simplement `Bar.prototype = Foo.prototype` résultera aux deux objets +> partageant le **même** prototype. Par conséquent, le changement du prototype d'un objet aura une +> incidence sur le prototype de l'autre ce qui, dans la plupart des cas, n'est pas l'effet désiré. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Assigner le prototype de Bar à une nouvelle instance de Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // S'assurer que Bar est le constructeur + Bar.prototype.constructor = Bar; + + var test = new Bar(); // crée une nouvelle instance de bar + + // La chaîne de prototypes qui en résulte + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +Dans le code ci-dessus, l'objet `test` va hériter à la fois de `Bar.prototype` et de `Foo.prototype`; par conséquent, il aura accès à la fonction `method` qui était définie sur `Foo`. Il aura également accès à la propriété `value` de la **seule** instance de `Foo` qui est son prototype. Il est important de noter que le `new Bar()` ne crée **pas** une nouvelle instance de `Foo`, mais réutilise celui attribué à son prototype; ainsi, toutes les instances de `Bar` se partageront la **même** propriété `value`. + +> **Remarque:** Ne **pas** utiliser `Bar.prototype = Foo`, car il ne pointera pas vers +> le prototype de `Foo` mais plutôt à l'objet-fonction `Foo`. donc, la +> chaîne de prototypes ira sur `Function.prototype` et non pas sur `Foo.prototype`; +> et donc, `method` ne sera pas disponible sur la chaîne de prototypes. + +### Recherche des propriétés + +Lors de l'accès aux propriétés d'un objet, JavaScript traversera la chaîne de prototypes **vers le haut** jusqu'à ce qu'il trouve une propriété avec le nom demandé. + +S'il atteint le sommet de la chaîne - à savoir `Object.prototype` - sans avoir trouvé la propriété spécifiée, la valeur [undefined](#core.undefined) sera retournée. + +### La propriété prototype + +Bien que la propriété prototype est utilisé par le langage pour construire la chaîne de prototypes, il est toujours possible de lui attribuer une valeur quelconque, mais les types primitifs seront simplement ignorés. + + function Foo() {} + Foo.prototype = 1; // aucun effet + +Assigner des objets, comme le montre l'exemple ci-dessus, va marcher, et permet la création dynamique de chaînes de prototypes. + +### Performance + +Les temps de recherche pour des propriétés qui sont en haut de la chaîne de prototypes peuvent avoir un impact négatif qui être significatif pour du code où la performance est critique. Essayer d'accéder à des propriétés inexistantes causera toujours la traversée complète de la chaîne de prototypes. + +De plus, [itérer](#object.forinloop) sur les propriétés d'un objet va causer l'énumération de **toutes** les propriétés qui se trouve sur la chaîne de prototype. + +### Extension des prototypes natifs + +Une mauvaise technique souvent utilisée est d'étendre `Object.prototype` ou un des prototypes intégrés. + +Cette technique est appelée [monkey patching][1] et casse l'*encapsulation*. Bien qu'utilisée par des cadriciels "frameworks" populaires tels que [Prototype][2], il n'existe aucune bonne raison pour encombrer les types intégrés avec des fonctionnalités supplémentaires *non standards*. + +La **seule** bonne raison d'étendre un prototype intégré est le rétroportage de caractéristiques des nouveaux moteurs JavaScript; par exemple, [`Array.forEach`][3]. + +### En conclusion + +Il est **essentiel** de comprendre le modèle d'héritage prototypique avant d'écrire du code complexe qui l'utilise. Soyez conscient de la longueur des chaînes de prototypes dans votre code; découpez les si nécessaire pour éviter de possible problèmes de performance. En outre, les prototypes natifs ne devraient **jamais** être étendus, sauf pour des raisons de compatibilité avec de nouvelles caractéristiques du langage JavaScript. + +[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/external/JavaScript-Garden/doc/fr/other/timeouts.md b/external/JavaScript-Garden/doc/fr/other/timeouts.md new file mode 100644 index 0000000..f06623e --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/other/timeouts.md @@ -0,0 +1,130 @@ +### `setTimeout` et `setInterval` + +Puisque JavaScript est asynchrone, il est possible de programmer l'exécution future d'une fonction en utilisant les fonctions `setTimeout` et `setInterval`. + +> **Remarque:** Les temps d'attente "timeouts" ne font **pas** partie de la norme ECMAScript. Ils sont implémentés dans les [BOM ou DOM Niveau 0][1], qui ne ne sont ni définies ni documentés formellement. +> Aucune spécification recommandée n'a été publiée jusqu'à présent, cependant, ils sont en voie de normalisation par [HTML5][2]. +> En raison de cela, l'implémentation peut varier entre navigateurs et moteurs. + + function foo() {} + var id = setTimeout(foo, 1000); // retourne un nombre > 0 + +Quand `setTimeout` est appelé, il renvoie l'identifiant de la temporisation et fixe la date de d'exécution de `foo` **approximativement** mille millisecondes dans le future. `foo` sera exécuté **une seule** fois. + +La résolution de l'horloge du moteur JavaScript exécutant le code, le fait que JavaScript est mono-thread, et la possibilité qu'autre code en cours d'exécution peut bloquer le fil "thread", font qu'il n'est **pas** possible de déterminer le temps exact d'attente spécifié dans l'appel `setTimeout`. + +La fonction passée en tant que premier paramètre sera appelé par l'*objet global*, ce qui signifie que [`this`](#function.this) à l'intérieur de la fonction appelée fait référence à l'objet global. + + function Foo() { + this.value = 42; + this.method = function() { + // this réfère a l'boject global + console.log(this.value); // enregistre undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **Remarque:** Comme `setTimeout` prend un **objet fonction** comme premier paramètre, une erreur commune est d'écrire `setTimeout(foo(), 1000)`, qui utilisera la **valeur de retour** de l'appel `foo` et non **pas** `foo`. Cette erreur est, la plupart du temps, silencieuse, car lorsque la fonction retourne `undefined`, `setTimeout` ne produira **pas** d'erreur. + +### Empilement des appels avec `setInterval` + +`setTimeout` exécute la fonction une seule fois. `setInterval` - comme son nom le suggère - exécutera la fonction toutes les 'X' millisecondes, mais son utilisation est découragée. + +Lorsque que du code en cours d'exécution bloque la temporisation, `setInterval` continuera a émettre plusieurs appels à la fonction spécifiée. Cela peut, en particulier avec un petit intervalle, résulter à un empilement d'appels de fonction. + + function foo(){ + // qq chose qui bloque pendant 1 seconde + } + setInterval(foo, 100); + +Dans le code ci-dessus, `foo` sera appelé une fois et bloquera pendant une seconde. + +Pendant ce temps, `setInterval` va continuer à planifier les appels à la fonction. Quand `foo` se termine, il y aura déjà **dix** autres appels qui attendent pour s'exécuter. + +### Traiter le code bloquant éventuel + +La solution la plus simple et qui offre le plus de contrôle est d'utiliser `setTimeout` dans la fonction elle-même. + + function foo(){ + // qq chose qui bloque pendant 1 seconde + setTimeout(foo, 100); + } + foo(); + +Non seulement cela encapsule l'appel `setTimeout`, mais il empêche également l'empilement des appels et donne un contrôle supplémentaire. La fonction `foo` elle-même peut maintenant décider si elle veut s'exécuter à nouveau ou non. + +### Effacer un délais d'attente + +L'effacement des délais d'attente et des intervalles fonctionne en transmettant l'identifiant retourné par la fonction `setTimeout` ou `setInterval` à `clearTimeout` ou `clearInterval`, respectivement. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Effacer tous les délais d'attente + +Comme il n'existe pas de méthode intégrée pour effacer tous les délais d'attente et/ou intervalles, il est nécessaire d'utiliser la force brute pour obtenir cette fonctionnalité. + + // Effacement de "tous" les délais d'attente + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Mais il pourrait encore y avoir des délais d'attente qui ne sont pas effacés par ce nombre arbitraire. +Une autre façon de faire découle du fait que l'identifiant donné à un délai d'attente est incrémenté à chaque fois que vous appelez `setTimeout`. + + // Effacement de "tous" les délais d'attente + var identifiantLePlusGrand = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= identifiantLePlusGrand; i++) { + clearTimeout(i); + } + +Même si cela fonctionne sur tous les principaux navigateurs d'aujourd'hui, il ne est pas précisé que les identifiants doivent être ordonnés de cette façon et cela peut changer. Par conséquent, il est plutôt recommandé de garder une trace de tous les identifiant crées, pour qu'ils puissent être effacées spécifiquement. + +### Utilisation cachée de `eval` + +`setTimeout` et `setInterval` peuvent également prendre une chaîne de caractères comme premier paramètre. +Cette fonctionnalité ne devrait **jamais** être utilisée car elle utilise `eval` en interne. + +> **Remarque:** Les mécanismes précis pour quand une chaîne est passée peuvent différer entre les diverses implémentations de JavaScript. +> Par exemple, JScript de Microsoft utilise le constructeur `Function` à la place de `eval`. + + function foo() { + // sera appelé + } + + function bar() { + function foo() { + // ne sera jamais appelé + } + setTimeout('foo()', 1000); + } + bar(); + +Puisque `eval` n'est pas appelé [directement](#core.eval), la chaîne passé à `setTimeout` sera exécutée dans la *portée globale*; ainsi, la variable `foo` locale à `bar` ne sera pas utilisée. + +Il est aussi recommandé de ne **pas** utiliser une chaîne pour passer des arguments à la fonction qui sera appelée par l'une des fonctions de temporisation. + + function foo(a, b, c) {} + + // ne JAMAIS faire cela + setTimeout('foo(1, 2, 3)', 1000) + + // utiliser plutôt une fonction anonyme + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +>**Remarque:** Bien qu'il soit également possible d'utiliser la syntaxe `setTimeout(foo, 1000, 1, 2, 3)`, elle n'est pas recommandée, car son utilisation peut entraîner des erreurs subtiles lorsqu'elle est utilisée avec des [méthodes](#function.this). +>En outre, la syntaxe peut ne pas fonctionner dans certaines implémentations de JavaScript. Par exemple, Internet Explorer de Microsoft [ne passe **pas** les arguments directement à la fonction de rappel][3]. + +### En conclusion + +Une chaîne ne devrait **jamais** être passée comme paramètre de `setTimeout` ou `setInterval` pour passer des arguments à la fonction appelée. C'est un clair signe de **mauvais** code. Il faut appeler une *fonction anonyme* qui se charge d'appeler la fonction réelle avec les arguments nécessaires. + +En outre, l'utilisation de `setInterval` doit être évitée car son programmateur n'est pas bloqué par le code en train de s'exécuter. + +[1]: http://www.nczonline.net/blog/2009/09/29/web-definitions-dom-ajax-and-more/ "Web definitions: DOM, Ajax, and more" +[2]: http://www.w3.org/TR/2014/WD-html5-20140617/webappapis.html#timers "6 Web application APIs - HTML5" +[3]: http://msdn.microsoft.com/en-us/library/ie/ms536753(v=vs.85).aspx "setTimeout method (Internet Explorer)" diff --git a/external/JavaScript-Garden/doc/fr/types/casting.md b/external/JavaScript-Garden/doc/fr/types/casting.md new file mode 100644 index 0000000..a0158f6 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/types/casting.md @@ -0,0 +1,62 @@ +## Changements de types + +JavaScript est un langage *faiblement typé*, il appliquera la *coercition de type* partout où c'est possible. + + // Ceux-ci sont vrais + new Number(10) == 10; // Objet Number est converti + // en un nombre primitif via un appel implicite + // à la méthode Number.prototype.valueOf + + 10 == '10'; // Strings est converti en Number + 10 == '+10 '; // Encore aussi fou + 10 == '010'; // Et encore + isNaN(null) == false; // null est converti en 0 + // ce qui, bien sûr, n'est pas NaN + + // Ceux-ci sont faux + 10 == 010; + 10 == '-10'; + +> **Remarque ES5:** Les nombres littéraux qui commencent avec un '0' sont interprétés comme octal (Base 8). L'octal a été **retiré** dans ECMAScript 5 en mode stricte. + +Pour éviter les problèmes ci-dessus, l'utilisation de l'[opérateur d'égalité stricte](# types.equality) est **fortement** recommandé. +Bien que cela évite beaucoup de pièges communs, il y en reste encore beaucoup. Tous ces pièges découlent de la faiblesse du système de typage de JavaScript. + +### Constructeurs de types internes + +Les constructeurs de types internes comme `Number` et `String` se comportent différemment suivant s'ils sont utilisés avec ou sans le mot clé `new`. + + new Number(10) === 10; // Faux, Object et Number + Number(10) === 10; // Vrai, Number et Number + new Number(10) + 0 === 10; // Vrai, due à la reconversion implicite + +L'utilisation d'un type intégré comme `Number` en tant que constructeur va créer une nouvel objet `Number`. Mais sans le mot clé `new`, `Number` se comportera comme un convertisseur. + +De plus, passer des valeurs littérales ou des non-objets se traduira par encore plus de coercition de type. + +La meilleure option est de forcer **explicitement** le type à l'un des trois types possibles . + +### Forcer à String + + '' + 10 === '10'; // vrai + +En faisant précéder une **chaîne vide**, une valeur peut facilement être converti en une chaîne. + +### Forcer à Number + + +'10' === 10; // vrai + +L'utilisation de l'opérateur **plus unaire** converti une valeur en nombre. + +### Forcer à Boolean + +L'utilisation double de l'opérateur **non** converti une valeur en booléen. + + !!'foo'; // vrai + !!''; // faux + !!'0'; // vrai + !!'1'; // vrai + !!'-1' // vrai + !!{}; // vrai + !!true; // vrai + diff --git a/external/JavaScript-Garden/doc/fr/types/equality.md b/external/JavaScript-Garden/doc/fr/types/equality.md new file mode 100644 index 0000000..3c1c09d --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/types/equality.md @@ -0,0 +1,58 @@ +## Égalité et comparaisons + +JavaScript a deux façons de comparer les valeurs des objets pour égalité. + +### L'opérateur d'égalité + +L'opérateur d'égalité se compose de deux signes égal: `==`. + +JavaScript est un langage au *typage faible* "weak typing". Cela signifie que l'opérateur d'égalité va convertir les deux opérandes en un même type afin de les comparer. + + "" == "0" // faux + 0 == "" // vrai + 0 == "0" // vrai + false == "false" // faux + false == "0" // vrai + false == undefined // faux + false == null // faux + null == undefined // vrai + " \t\r\n" == 0 // vrai + +Le tableau ci-dessus montre les résultats de la coercition de type, et c'est la raison principale pourquoi l'utilisation de `==` est largement considéré comme une mauvaise pratique. Les règles de conversion de types compliquées introduisent des bogues difficiles à dépister. + +### L'opérateur d'égalité stricte + +L'opérateur d'égalité stricte se compose de **trois** signes égal: `===`. + +Il fonctionne comme l'opérateur d'égalité normale, sauf que l'égalité stricte ne converti **pas** le types de ses opérandes. + + "" === "0" // faux + 0 === "" // faux + 0 === "0" // faux + false === "false" // faux + false === "0" // faux + false === undefined // faux + false === null // faux + null === undefined // faux + " \t\r\n" === 0 // faux + +Les résultats ci-dessus sont beaucoup plus clairs et permettent la rupture précoce de code. +Cela durcit le code jusqu'à un certain degré, et améliore la performance dans le cas où les opérandes sont de types différents. + +### Comparaison d'objets + +Bien que `==` et `===` sont appelés **opérateurs d'égalité**, ils se comportent différement quand au moins un des opérandes est un objet `Object`. + + {} === {}; // faux + new String('foo') === 'foo'; // faux + new Number(10) === 10; // faux + var foo = {}; + foo === foo; // vrai + +En effet, les deux opérateurs comparent l'**identité** et non pas l'**égalité**. Autrement dit, ils comparent pour la même **instance** de l'objet, tout comme `is` en Python, ou la comparaison de pointeur en C. + +### En conclusion + +Il est fortement recommandé de n'utiliser que l'opérateur d'**égalité stricte**. Dans les cas où les types ont à être convertis, cela devraient être fait [explicitement](#types.casting) et non pas laissé aux règles complexes de coercition de type du langage. + + diff --git a/external/JavaScript-Garden/doc/fr/types/instanceof.md b/external/JavaScript-Garden/doc/fr/types/instanceof.md new file mode 100644 index 0000000..ba733e4 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/types/instanceof.md @@ -0,0 +1,35 @@ +## L'opérateur `instanceof` + +L'opérateur `instanceof` (instance de) compare les constructeurs de ses deux opérandes. Il est seulement utile pour comparer des objets faits sur mesure. Utilisé sur les types intégrés, il est +aussi inutile que l'[opérateur typeof](# types.typeof). + +### Comparer des objets personnalisés + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // vrai + new Bar() instanceof Foo; // vrai + + // Ceci définit simplement Bar.prototype à l'objet de fonction Foo, + // mais pas à une instance réelle de Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // faux + +### Utiliser `instanceof` avec des types natifs + + new String('foo') instanceof String; // vrai + new String('foo') instanceof Object; // vrai + + 'foo' instanceof String; // faux + 'foo' instanceof Object; // faux + +Une chose importante à noter ici est que `instanceof` ne fonctionne pas sur les objets qui proviennent de différents contextes JavaScript (par exemple, différents documents +dans un navigateur web), car leurs constructeurs ne seront pas exactement le même objet. + +### En conclusion + +L'opérateur `instanceof` devrait **seulement** être utilisé sur des objets crées sur mesure provenant du même contexte JavaScript. +Tout comme l'opérateur [`typeof`](#types.typeof), chaque autre utilisation de celui-ci devrait être **évitée**. + diff --git a/external/JavaScript-Garden/doc/fr/types/typeof.md b/external/JavaScript-Garden/doc/fr/types/typeof.md new file mode 100644 index 0000000..186bfb3 --- /dev/null +++ b/external/JavaScript-Garden/doc/fr/types/typeof.md @@ -0,0 +1,66 @@ +## L'opérateur `typeof` + +L'opérateur `typeof` (avec [`instanceof`](#types.instanceof)) est probablement le plus grand défaut de design de JavaScript, car il est presque **complètement cassé**. + +Bien que `instanceof` (instance de) a quelques utilisations limitées, `typeof` (type de) n'a qu'un seul cas pratique d'utilisation, et ce cas n'est **pas** la vérification du type d'un objet. + +> **Remarque:** Alors que `typeof` peut être appelé comme une fonction, i.e. `typeof(obj)`, ce n'est pas un appel de fonction. +> Les parenthèses se comportent comme d'habitude et la valeur de retour sera utilisé comme opérande de l'opérateur `typeof`. La *fonction* `typeof` n'existe pas. + +### table de types JavaScript + + Valeur 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 pour Nitro/V8) + new RegExp("meow") RegExp object (function pour Nitro/V8) + {} Object object + new Object() Object object + +Dans la table ci-dessus, *Type* se réfère à la valeur retournée pas l'opérateur `typeof`, et comme on peut le voir clairement, cette valeur est incohérente. + +*Class* se réfère à la valeur de la propriété interne `[[Class]]` d'un objet. + +> **D'après la spécification:** La valeur de `[[Class]]` peut être l'une des chaînes suivantes: `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +Pour récupérer la valeur de `[[Class]]`, on peut utiliser la méthode `toString` de `Object.prototype`. + +### La classe d'un objet + +La spécification donne exactement un moyen d'accéder à la valeur `[[Class]]`: via `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'); // vrai + is('String', new String('test')); // vrai + +Dans l'exemple ci-dessus, `Object.prototype.toString` est appelé avec la valeur [this](#function.this) pointant sur l'objet dont on cherche a récupérer la valeur de `[[Class]]`. + +> **Remarque ES5:** Pour plus de commodité la valeur de retour de `Object.prototype.toString` de `null` et `undefined` a été **changée** de `Object` à `Null` et `Undefined` avec ECMAScript 5. + +### Test pour les variables indéfinies + + typeof foo !== 'undefined' + +Ce qui précède vérifie si `foo` a été effectivement déclarée. Juste référencer la variable résulterait en une erreur de référence `ReferenceError`. C'est la seule chose pour laquelle `typeof` est réellement utile. + +### En conclusion + +Pour vérifier le type d'un objet, il est fortement recommandé d'utiliser `Object.prototype.toString` parce que c'est le seul moyen fiable de le faire. +Comme représenté dans la table de type ci-dessus, certaines valeurs de retour de `typeof` ne sont pas définies dans la spécification; et donc, elles peuvent différer entre les implémentations. + +If faut éviter d'utiliser `typeof`, sauf pour vérifier si une variable est définie. + diff --git a/external/JavaScript-Garden/doc/hu/array/constructor.md b/external/JavaScript-Garden/doc/hu/array/constructor.md new file mode 100644 index 0000000..57a5a9c --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/array/constructor.md @@ -0,0 +1,34 @@ +## Az `Array` konstruktor + +Mivel az `Array` konstruktora kétértelműen bánik a paraméterekkel, melegen +ajánlott mindig a tömb literált - `[]` jelölés - használni új tömbök létrehozásakor. + + [1, 2, 3]; // Eredmény: [1, 2, 3] + new Array(1, 2, 3); // Eredmény: [1, 2, 3] + + [3]; // Eredmény: [3] + new Array(3); // Eredmény: [] + new Array('3') // Eredmény: ['3'] + +Abban az esetben, ha ez a konstruktor csak egy `szám` paramétert kap, akkor +visszatérési értékül egy olyan tömböt fog létrehozni amelynek a `length` mezője +akkorára van beállítva, ahogy azt megadtuk az argumentumban. Megjegyzendő hogy +**csak** a `length` tulajdonság lesz ekkor beállítva; az egyes indexek külön-külön +nem lesznek inicializálva. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // hamis, nincs ilyen index + +A tömb hosszának közvetlen állítása amúgy is csak elég kevés esetben +használható értelmesen, mint például alább, hogyha el akarjuk kerülni a +`for ciklus` használatát egy string ismétlésekor. + + new Array(count + 1).join(ismetlendoString); + +### Összegzésül + +Az `Array` konstruktor közvetlen használata erősen kerülendő. A literálok használata +elfogadott inkább, mivel rövidebbek, tisztább a szintaxisuk és olvashatóbb kódot +eredményeznek. + diff --git a/external/JavaScript-Garden/doc/hu/array/general.md b/external/JavaScript-Garden/doc/hu/array/general.md new file mode 100644 index 0000000..f7fa98e --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/array/general.md @@ -0,0 +1,58 @@ +## Tömb iteráció és tulajdonságok + +Habár a tömbök a JavaScriptben objektumok, nincsen jó ok arra, hogy a [`for in`](#object.forinloop) ciklussal járjuk be őket. +Valójában sokkal több jó ok van arra, hogy **miért ne** így tegyünk. + +> **Megjegyzés:** A JS tömbök **nem** *asszociatív tömbök*. A JavaScriptben egyedül +> az [objektumokkal](#object.general) lehet kulcsokat értékekhez rendelni. Ráadásul +> amíg az asszociatív tömbök **megőrzik** a sorrendjüket, az objektumok **nem**. + +Mivel a `for in` ciklus a prototípus láncon levő összes tulajdonságon végigmegy, +és mivel az egyetlen út ennek megkerülésére a [`hasOwnProperty`](#object.hasownproperty) használata, így majdnem **hússzor** +lassabb mint egy sima `for` ciklus. + +### Iteráció + +Annak érdekében, hogy a legjobb teljesítményt érjük el a tömbökön való iteráció során, +a legjobb hogyha a klasszikus `for` ciklust használjuk. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Még egy érdekesség van a fenti példában, ami a tömb hosszának cachelését végzi +a `l = list.length` kifejezés használatával. + +Habár a `length` tulajdonság mindig magán a tömbön van definiálva, még mindig +lehet egy kis teljesítmény kiesés amiatt hogy minden iterációban újra meg kell +keresni ezt a tulajdonságot. Persze a legújabb JavaScript motorok **talán** +használnak erre optimalizációt, de nem lehet biztosan megmondani, hogy ahol a kódunk +futni fog, az egy ilyen motor-e vagy sem. + +Valójában, a cachelés kihagyása azt eredményezheti, hogy a ciklusunk csak +**fele olyan gyors** lesz mintha a cachelős megoldást választottuk volna. + +### A `length` mező + +Míg a `length` mező *getter* függvénye egyszerűen csak visszaadja a tömbben +levő elemek számát, addig a *setter* függvény használható arra (is), hogy +**megcsonkítsuk** a tömbünket. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo.push(4); + foo; // [1, 2, 3, undefined, undefined, undefined, 4] + +Egy rövidebb hossz alkalmazása csonkítja a tömböt. A nagyobb hossz megadása +értelemszerűen növeli. + +### Összegzésül + +A megfelelő teljesítmény érdekében, a `for` ciklus használata és a length cachelése +ajánlott. A `for in` ciklus használata a tömbökön a rosszul megírt kód jele, amely +tele lehet hibákkal, és teljesítményben sem jeleskedik. + diff --git a/external/JavaScript-Garden/doc/hu/core/delete.md b/external/JavaScript-Garden/doc/hu/core/delete.md new file mode 100644 index 0000000..89b95b4 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/core/delete.md @@ -0,0 +1,91 @@ +## A `delete` Operátor + +Röviden, *lehetetlen* globális változókat, függvényeket és olyan dolgokat törölni +JavaScriptben amelyeknek a `DontDelete` attribútuma be van állítva. + +### Globális kód és Függvény kód + +Amikor egy változó/függvény, globális vagy +[függvény hatókörben](#function.scopes) van definiálva, +akkor az vagy az Aktivációs (Activation) vagy a Globális (Global) objektum egyik mezőjeként +jön létre. Az ilyen mezőknek van egy halom attribútuma, amelyek közül az egyik +a `DontDelete`. A változó és függvény deklarációk a globális vagy függvény kódon +belül mindig `DontDelete` tulajdonságú mezőket hoznak létre, így nem lehet őket +törölni. + + // globális változó + var a = 1; // A DontDelete be lett állítva + delete a; // hamis + a; // 1 + + // függvény: + function f() {} // A DontDelete be lett állítva + delete f; // hamis + typeof f; // "function" + + // új értékadással sem megy + f = 1; + delete f; // hamis + f; // 1 + +### Explicit mezők + +Az expliciten beállított mezőket persze normálisan lehet törölni. + + // expliciten beállított mező + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // igaz + delete obj.y; // igaz + obj.x; // undefined + obj.y; // undefined + + +A fenti példában az `obj.x` és `obj.y` törölhető, mivel nincs `DontDelete` +attribútuma egyik mezőnek sem. Ezért működik az alábbi példa is. + + // működik, kivéve IE-ben + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // igaz - egy globális változó + delete GLOBAL_OBJECT.a; // igaz + GLOBAL_OBJECT.a; // undefined + +Itt egy trükköt használunk az `a` törlésére. A [`this`](#function.this) itt +a Globális objektumra mutat, és expliciten bevezetjük rajta az `a` változót, mint +egy mezőjét, így törölni is tudjuk. + +Mint az szokás, a fenti kód egy kicsit bugos IE-ben (legalábbis 6-8-ig). + +### Függvény argumentumok és beépített dolgaik + +A függvény argumentumok, az [`arguments` objektum](#function.arguments) +és a beépített mezők szintén `DontDelete` tulajdonságúak. + + // függvény argumentumok és mezők + (function (x) { + + delete arguments; // hamis + typeof arguments; // "object" + + delete x; // hamis + x; // 1 + + function f(){} + delete f.length; // hamis + typeof f.length; // "number" + + })(1); + +### Vendég (host) objektumok + +A `delete` operátor működése megjósolhatatlan a vendég objektumokra. A specifikáció +szerint ezek az objektumok szükség szerint bármilyen viselkedést implementálhatnak. + +(A ford.: Vendég objektumok azok az objektumok, amelyek nincsenek konkrétan +meghatározva az ES aktuális verziójú specifikációjában, pl. a window) + +### Összegzésképp + +A `delete` működése helyenként megjósolhatatlan, így biztonsággal csak olyan +objektumok mezőin használhatjuk, amelyeket expliciten mi állítottunk be. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/core/eval.md b/external/JavaScript-Garden/doc/hu/core/eval.md new file mode 100644 index 0000000..9de5d3b --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/core/eval.md @@ -0,0 +1,49 @@ +## Miért Ne Használjuk az `eval`-t + +Az `eval` (evil) funkció egy stringbe ágyazott JavaScript kódot futtat a +lokális scopeon belül. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Viszont az `eval` csak akkor viselkedik így, hogyha expliciten hívjuk meg +*és* a meghívott funkció neve valóban `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +Az `eval` használata kerülendő. A "felhasználása" az esetek 99.9%-ban +**mellőzhető**. + +### Az `eval` ezer arca + +A `setTimeout` és `setInterval` nevű [timeout függvények](#other.timeouts) is +tudnak úgy működni, hogy első paraméterükként egy stringbe ágyazott kódot várnak. +Ez a string **mindig** a globális hatókörben lesz végrehajtva, mivel az `eval`t +így nem direktben hívjuk meg. + +### Biztonsági problémák + +Az `eval` azért is veszélyes, mert **bármilyen** JS kódot végrehajt, amit odaadunk +neki. Éppen ezért **sose** használjuk olyan kódok végrehajtására amiknek az eredete +nem megbízható/ismeretlen. + +### Összegzésül + +Soha ne használjunk `eval`t. Bármilyen kód működése, teljesítménye, ill. biztonsága +megkérdőjelezhető, amely használja ezt a nyelvi elemet. Semmilyen megoldás +használata **nem ajánlott** amely első sorban `eval`ra épül. Ekkor egy *jobb +megoldás* szükségeltetik, amely nem függ az `eval`tól. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/core/semicolon.md b/external/JavaScript-Garden/doc/hu/core/semicolon.md new file mode 100644 index 0000000..fddd1ac --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/core/semicolon.md @@ -0,0 +1,119 @@ +## Automatic Semicolon Insertion + +Bár a JavaScriptnek látszólag C-s szintaxisa van, **mégsem** kötelező benne +kirakni a pontosvesszőket, így (helyenként) kihagyhatóak a forrásból. +(A ford.: hiszen interpretált nyelv lévén nincsenek fordítási hibák, így +nyelvi elemek meglétét sem tudja erőltetni a nyelv) + +Itt jön a csel, hogy ennek ellenére a JavaScript csak pontosvesszőkkel +értelmezi megfelelően a beírt kódot. Következésképp, a JS **automatikusan** +illeszti be a pontosvesszőket (megpróbálja kitalálni a gondolataink) +azokra a helyekre, ahol amúgy emiatt értelmezési hibába futna. + + var foo = function() { + } // értelmezési hiba, pontosvessző kéne + test() + +Az automatikus beillesztés megtörténik, ezután így értelmeződik a kód + + var foo = function() { + }; // nincs hiba, mindenki örül + test() + +Az automatikus beillesztés (ASI) a JavaScript (egyik) **legnagyobb** design +hibája, mivel igen... *meg tudja* változtatni a kód értelmezését + +### Hogyan Működik + +Az alábi kódban nincsen pontosvessző, így az értelmező (parser) feladata kitalálni, +hogy hova is illessze be őket. + + (function(window, undefined) { + function test(options) { + log('testing!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'hosszú string az argumentumban', + 'még még még még még hossszabbbbbbb' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Alább mutatjuk a "kitalálós" játék eredményét. + + (function(window, undefined) { + function test(options) { + + // Nincs beillesztés, a sorok össze lettek vonva + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- beillesztés + + options.value.test( + 'hosszú string az argumentumban', + 'még még még még még hossszabbbbbbb' + ); // <- beillesztés + + return; // <- beillesztés, eltörik a return kifejezésünk + { // blokként értelemződik + + // név: kifejezés formátumban értelmeződik + foo: function() {} + }; // <- beillesztés + } + window.test = test; // <- beillesztés + + // Ezeket a sorokat összeilleszti + })(window)(function(window) { + window.someLibrary = {}; // <- beillesztés + + })(window); //<- beillesztés + +> **Megjegyzés:** A JavaScript értelmező nem tudja "korrektül" kezelni azokat +> a return kifejezéseket, amelyek után közvetlen új sor áll. Habár ez nem biztos hogy +> szükségszerűen az ASI hibájából történik, azért nem egy túl kellemes mellékhatás. + +Az értelmező drasztikusan megváltoztatta a fenti kódot. A legtöbb esetben a +beillesztő **rosszul** tippel. + +(A ford.: Semmilyen nyelvben sem jó, hogyha hagyjuk hogy a gép találja ki mit +szerettünk volna írni. Néma gyereknek az anyja sem érti a kódját ugye.) + +### Kezdő Zárójelek + +Az értelmező **nem** rak be új pontosvesszőt, hogyha a sor eleje (nyitó) zárójellel kezdődik. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Ez a kód egy sorként értelmeződik + + log('testing!')(options.list || []).forEach(function(i) {}) + +Az esélyek arra **elég** magasak, hogy a `log` **nem** egy függvényt fog visszatéríteni; így a fenti kód egy `TypeError` típusú hibát fog dobni +`undefined is not a function` üzenettel. + +### Összefoglalásképp + +Szükségszerűen **soha** ne hagyjuk ki a pontoszvesszőket. Nem árt a kapcsos +zárójeleket is ugyanazon a soron tartani, mint amelyiken az utasítást elkezdtük, +így nem ajánlott az egysoros `if` / `else` kifejezések kedvéért elhagyni +őket. Ezek a szempontok nem csak a kódot (és annak olvashatóságát) tartják +konzisztensen, de megelőzik azt is hogy a JavaScript értelmező valamit rosszul +"találjon ki". \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/core/undefined.md b/external/JavaScript-Garden/doc/hu/core/undefined.md new file mode 100644 index 0000000..316feb1 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/core/undefined.md @@ -0,0 +1,77 @@ +## Az `undefined` és a `null` + +A JavaScript két értéket is tartogat a semmi kifejezésére, ezek a `null` és az +`undefined` és ezek közül az utóbbi a hasznosabb. + +### Az `undefined` + +Ha az előbbi bevezetőtől nem zavarodtál volna össze; az +`undefined` egy típus amelynek pontosan egy értéke van, az `undefined`. + +A nyelvben szintén van egy `undefined` nevű globális változó amelynek az értékét +hogy-hogy nem `undefined`-nak hívják. Viszont ez a változó **nem** konstans vagy +kulcsszó a nyelvben. Ez azt jeletni hogy az *értéke* könnyedén felülírható. + +> **ES5 Megjegyzés:** Az `undefined` ECMAScript 5-ben **többé** *nem felülírható* +> strict módban, bár a neve továbbra is eltakarható, például egy saját függvénnyel +> aminek a neve éppen `undefined`. + +Itt van pár példa, hogy mikor is találkozhatunk az `undefined` értékkel: + + - Az `undefined` globális változó elérésekor + - Egy deklarált, de nem inicializált változó elérésekor. + - Egy függvény hívásakor ez a visszatérési érték, `return` utasítás híján. + - Egy olyan `return` utasítás lefutásakor, amely nem térít vissza értéket. + - Nem létező mezők lekérésekor. + - Olyan függvény paraméterek elérésekor amelyeknek a hívó oldalon nem kaptak értéket. + - Bármikor amikor az `undefined` érték van valaminek beállítva. + - Bármelyik `void(kifejezés)` utasítás futtatásakor. + +### `undefined` megőrzési trükkök + +Mivel az `undefined` nevű globális változó csak egy másolatot tárol az +`undefined` elnevezésű értékből, az értékének megváltoztatása **nem** írja +felül az eredeti `undefined` *típus* értékét. + +Ezért, ha valamilyen értékkel össze szeretnénk hasonlítani az `undefined` értéket, +nem árt hogyha először magát az `undefined`-ot el tudjuk érni. + +Egy gyakori technika annak érdekében hogy megvédjük a kódunkat az +`undefined` lehetséges felüldefiniálásaitól, hogy egy [névtelen (wrapper)](#function.scopes) függvénybe +csomagoljuk az egész kódunkat, amelynek lesz egy direkt üres paramétere. + + var undefined = 123; + (function(something, foo, undefined) { + // az undefined ebben a hatókörben + // megint valóban az `undefined` értékre referáll. + + })('Hello World', 42); + +Egy másik módja ennek, hogy használunk egy "üres" deklarációt a wrapper függvényen +belül. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Az egyetlen különbség ebben a változatban, hogyha minifikáljuk ezt a kódot, +és nem definiálunk további változókat ezen a részen belül, akkor ezzel a +változattal extra 4 byte "veszteséget" szenvedünk el. + +### Mikor használjunk `null`t + +Miközben az `undefined` a natív JavaScript megvalósításokban inkább a (más +nyelvekben levő) tradícionális *null* helyett használandó, azalatt maga a `null` +inkább csak egy különböző adattípusnak számít, mindenféle különös jelentés nélkül. + +Egy pár belső JavaScriptes megoldásban ugyan használják (ahol pl. a prototípus lánc végét a `Foo.prototype = null` beállítással jelölik), de a legtöbb esetben ez +felcserélhető az `undefined`-al. + +(A ford.: A `null` annak az esetnek a jelölésére hasznos, amikor +egy referencia típusú változót deklarálunk, de még nem adunk neki értéket. Pl. a +`var ezObjektumLesz = null` kifejezés ezt jelöli. Tehát a null leginkább +kezdeti értékként állja meg a helyét, minden másra ott az `undefined`) + diff --git a/external/JavaScript-Garden/doc/hu/function/arguments.md b/external/JavaScript-Garden/doc/hu/function/arguments.md new file mode 100644 index 0000000..0a0a1e6 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/arguments.md @@ -0,0 +1,120 @@ +## Az `arguments` objektum + +Minden függvényhatókörben hozzáférhető az `arguments` nevű speciális változó, +amely azon argumentumok listáját tartalmazza, amelyekkel a függvényt meghívták. + +> **Megjegyzés:** Abban a trükkös esetben, hogyha a függvényhatókörön belül valahogy +> definiáljuk az `arguments`-et mint nevet, akár változóként (`var`ral), vagy a függvény +> paramétereként, akkor ez a speciális `arguments` objektum nem lesz létrehozva. + +Lehet hogy úgy néz ki, de az `arguments` objektum **nem** egy `tömb`. Látszólag hasonlít rá, +mivel van például egy `length` nevű mezője, de igazából nem az `Array.prototype`-ból "származik", +hanem tisztán az `Object`-ből. + +Itt jön a trükk lényege, hogy ennek köszönhetően **nem** használhatóak rajta a standard +tömb műveletek mint például a `push`, `pop` vagy a `slice`. Míg a sima `for` ciklusos iterálás +működik itt is, ahhoz hogy az előbb említett műveleteket is tudjuk rajta használni, át kell +konvertálni egy valódi `Array` objektummá. + +### Tömbbé konvertálás + +Ez a kódrészlet egy új `Array` objektummá varázsolja az emlegetett `arguments` szamarat. + + Array.prototype.slice.call(arguments); + +De, ez a konverzió meglehetősen **lassú**, így egyáltalán **nem ajánlott** teljesítmény kirtikus +alkalmazások írásakor. + +### Argumentumok kezelése + +A következő módszer ajánlott arra az esetre, hogy ha az egyik függvény paramétereit egy-az-egyben +át szeretnénk adni egy másik függvény számára. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // sok okos kód ide + } + +Egy másik trükk arra, hogy teljesen független wrapper függvényeket gyártsunk, a `call` +és `apply` együttes használata. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Elkészíti a "method" (this) független verzióját + // Ezeket kapja paraméterül: this, arg1, arg2...argN + Foo.method = function() { + + // Eredmény: Foo.prototype.method.call(this, arg1, ...argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + +### Paraméterek és argumentum indexek + +A háttérben az `arguments` objektum minden egyes indexére (elemére) egy *getter* és egy *setter* +függvényt is kap, csak úgy ahogy a függvény paramétereit is felül tudjuk írni, illetve eltudjuk érni. + +Ennek eredményeképp, az `arguments` objektumon véghezvitt változtatások szinkronban +változtatják a függvény névvel ellátott paramétereit is. + + 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); + +### Teljesítmény mítoszok és trükkök + +Ahogy már azt korábban körvonalaztuk, az `arguments` objektum csak akkor nem jön létre, +hogyha a függvényhatókörön belül definiálunk egy változót ezzel a névvel, vagy a függvényünk +egyik paraméterének ezt a nevet választjuk. + +Azonban a *getterek* és *setterek* mindig létrejönnek, de ez ne zavarjon meg minket, mert +semmiféle befolyása nincs a teljesítményre, pláne olyan kódban ahol sokkal több mindennel +is foglalkozunk, mint az `arguments` objetkumhoz való hozzáférés. + +> **ES5 Megjegyzés:** Ezek a **getterek** és **setterek** nem jönnek létre strict módban. + +Habár, egyetlen eset van, amelynek komoly hatása lehet a kód teljesítményére a modern +JavaScript motorokban. Ez pedig az `arguments.callee` használata. + + function foo() { + // ..csinálunk valamit + arguments.callee; // ezzel a függvény objektummal + arguments.callee.caller; // és ennek a hívójával.. + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Így viszont nem lehet behelyettesíteni ide... + } + } + +A fenti kódban a `foo` helyére nem lehet egyszerűen behelyettesíteni a [függvény törzsét][1], +mivel a függvény törzsének fogalma kell legyen mind magáról, mind az ő hívójáról. Ez nem csak +hogy azt akadályozza meg, hogy a behelyettesítéssel nyerjünk egy kis többlet performanciát, +de az egységbe zárás elvét is erősen keresztbevágja, hiszen a függvény így erősen támaszkodni +fog a hívó környezetére (kontextusára). + +Emiatt is, az `arguments.callee`, vagy bármely mezőjének használata **erősen kerülendő**. + +> **ES5 Okoskodás:** Strict módban, az `arguments.callee` kifejezés egy `TypeError` hibát fog dobni, +> mivel a használata elavult. + +[1]: http://en.wikipedia.org/wiki/Inlining + + + + diff --git a/external/JavaScript-Garden/doc/hu/function/closures.md b/external/JavaScript-Garden/doc/hu/function/closures.md new file mode 100644 index 0000000..33d4e3b --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/closures.md @@ -0,0 +1,99 @@ +## Closure-ök és referenciák + +A JavaScript nyelv egyik legerőteljesebb tulajdonsága a *closure*-ök használatában rejlik. +Ezek használatával a hatókörök egymásba ágyazhatóak, és egy belső hatókör mindig hozzáfér +az őt körülvevő, külső hatókör változóihoz. Miután JavaScriptben egyetlen dologgal lehet +hatóköröket kifejezni, és ez a [függvény](#function.scopes) (bizony az if, try/catch és hasonló blokkok **nem** jelentenek új hatókört, mint pl. a Javaban), az összes funkció closure-ként szerepel. + +### Privát változók emulálása + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Ebben a példában a `Counter` **két** closure-rel tér vissza: az `increment` és +a `get` függvénnyel. Mind a két függvény **referenciát** tárol a `Counter` hatókörre, +és így mindketten hozzáférnek a `count` változóhoz, ami ebben a hatókörben lett +definiálva. + +### Miért működnek a privát változók? + +Mivel a JavaScriptben egyszerűen **nem** lehet hatókörre referálni, vagy hatókört +értékül adni, így ezért szintén lehetetlen elérni az iménti `count` változót a külvilág számára. +Egyetlen mód van a megszólítására, ezt pedig láttuk a fentebbi két closure-ön belül. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +A fentebbi kód **nem** fogja megváltoztatni a `Counter` hatókör `count` változóját, +mivel a `foo.hack` mező **nem abban** a hatókörben lett létrehozva. Ehelyett, okosan, +létre fogja hozni, vagy felül fogja írni a *globális* `count` változót (window.count). + +### Closure-ök használata ciklusokban + +Az egyik leggyakoribb hiba, amit el lehet követni, az a closure-ök ciklusokban való használata. +Annak is azon speciális esete amikor a ciklus indexváltozóját szeretnénk lemásolni a closure-ön belül. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +A fenti kódrészlet marhára **nem** a számokat fogja kiírni `0`-tól `9`-ig, de inkább +a `10`-et fogja tízszer kiírni. + +Ugyanis a belső *névtelen* függvény egy **referenciát** fog tárolni a külső `i` változóra, és +akkor, amikor végül a `console.log` sor lefut, a `for loop` már végzett az egész ciklussal, +így az `i` értéke `10`-re lesz beállítva. + +Ahhoz, hogy a várt működést kapjuk (tehát a számokat 0-tól 9-ig), szükségszerű az `i` változó +értékét **lemásolni**. + +### A referencia probléma elkerülése + +Az előző problémára megoldást úgy lehet jól adni, hogy az utasításoknak megfelelően +lemásoljuk a ciklusváltozót, úgy hogy a jelenlegi ciklusmagöt körbevesszük egy [névtelen +függvénnyel](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +A külső (wrapper) névtelen függvény így azonnal meghívódik az `i` ciklusváltozóval, mint paraméterrel, +és így mindig egy másolatot fog kapni az `i` változó **értékéről**, amit ő `e` néven emészt tovább. + +Így a `setTimeout`ban lévő névtelen függvény mindig az `e` nevű referenciára fog mutatni, aminek az értéke így már **nem** változik meg a ciklus futása során. + +Egy másik lehetséges út a megoldáshoz az, hogy egy wrapper függvényt visszatérítünk a setTimeoutból, aminek ugyanaz lesz a hatása, mint a fentebbi példának. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + + + + diff --git a/external/JavaScript-Garden/doc/hu/function/constructors.md b/external/JavaScript-Garden/doc/hu/function/constructors.md new file mode 100644 index 0000000..c955cce --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/constructors.md @@ -0,0 +1,128 @@ +## Konstruktorok + +Csak úgy, mint minden más, a konstruktorok működése szintén különbözik +a megszokottól. Itt minden függvényhívás amelyet a `new` kulcsszó előz meg, +konstruktor hívásnak számít. + +A `this` értéke a konstruktoron - hívott függvényen - belül az újonnan létrehozott objektumra +mutat. Az **új** objektum [prototípusa](#object.prototype) a konstruktor függvény `prototípusával` fog megegyezni. + +Ha a konstruktor függvényben nincs `return` utasítás, akkor automatikusan a `this` értékével tér vissza - a létrehozott objektummal. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +A fenti kódban a `Foo` függvényt, mint konstruktort hívjuk meg, ami a test változóban +egy új objektumot fog eredményezni. Ennek az objektumnak a `prototípusa` a Foo prototípusa lesz. + +Trükkös ugyan, de ha mégis van `return` utasítás az éppen konstruált függvényben, akkor +a függvény hívása az annak megfelelő értékkel fog visszatérni, de **csak** akkor, ha a +visszatérített érték `Objektum` típusú. + + function Bar() { + return 2; + } + new Bar(); // ez egy új üres objektum lesz: {}, a 2 helyett + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // ez a { foo: 1 } objektumot fogja eredményezni + +Hogyha kihagyjuk a `new` kulcsszó használatát, a függvény **nem** egy új objektummal fog visszatérni. + + function Foo() { + this.bla = 1; // ez a globális objektumon állít + } + Foo(); // undefined + +A [`this`](#function.this) JavaScript beli működésének köszönhetően, még ha le is +fut az előbbi kód, akkor a `this` helyére a *globális objektumot* képzeljük. + +### Gyárak (Factory-k) + +Ahhoz, hogy teljesen el tudjuk hagyni a `new` kulcsszó használatát, a konstruktor +függvény explicit értékkel kell visszatérjen. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Mindkét `Bar`-ra történő hívásmód ugyanazt fogja eredményezni. Kapunk általuk +egy újonnan létrehozott objektumot, amelynek lesz egy `method` nevű mezője, +ami egyébiránt egy [Closure](#function.closures). + +Azt is érdekes itt megjegyezni, hogy a `new Bar()` hívás **nem** befolyásolja a +visszatérített objektum prototípusát. Mivel a prototípus csak az újonnan +létrehozott objektumon létezik, amit a `Bar` nem térít vissza (mivel egy explicit +értéket ad vissza). + +A fenti példában nincs funkcionális különbség aközött hogy kiírjuk-e a `new` +varázsszót avagy nem. + +### Új objektumok létrehozása gyárakon keresztül + +Gyakran bevett módszer egy projetkben, hogy a `new` varázsszó használatát +teljesen elhagyjuk, mert a kiírásának elfelejtése bugokhoz vezetne. + +Ennek érdekében egy új objektum létrehozásához inkább egy gyárat kell +implementálni, és annak a belsejében létrehozni az új objektumot. + + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +A fenti kód ugyan ellenálló a hiányzó `new` kulcsszó hibáját illetően és +megfelelően használ [privát változókat](#function.closures), érdemes +megemlíteni a dolgok kontra részét is. + + 1. Több memóriát használ, mivel az így létrehozott objektumok **nem** + osztják meg a prototípusukat egymás között. + 2. A származtatás macerás, mivel a gyár kénytelen ilyenkor lemásolni + az összes származtatandó metódust egy másik objektumról, vagy ezt az objektumot + be kell állítsa a létrehozott új objektum prototípusának. + 3. Az a megközelítés miszerint egy kifelejtett `new` kulcsszó miatt eldobjuk + az objektum teljes prototípusát, ellenkezik a nyelv szellemiségével. + +### Összefoglaló + +A `new` varázsszó kihagyása ugyan bugokhoz vezethet, de ez **nem** megfelelő indok +arra hogy ezért eldobjuk a prototípusok használatát. Végeredményben mindig +az fog dönteni a különböző stílusok megválasztása között, hogy mire van +szüksége éppen az aktuális programunknak. Egy dolog azért elengedhetetlenül +fontos, ez pedig hogy megválasszuk melyik stílust fogjuk használni objektumok +létrehozásra, és ezt **konzisztensen** használjuk a teljes megoldáson keresztül. diff --git a/external/JavaScript-Garden/doc/hu/function/general.md b/external/JavaScript-Garden/doc/hu/function/general.md new file mode 100644 index 0000000..9b62b0e --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/general.md @@ -0,0 +1,48 @@ +## Függvény deklarációk és kifejezések + +A függvények JavaScriptben egyben objektumok is. Ez azt jelenti, hogy +ugyanúgy lehet őket passzolgatni, mint bármelyik más értékeket. Ezt a featuret +gyakran használják arra, hogy egy *névtelen (callback) függvényt* átadjunk +egy másik -aszinkron- függvény paramétereként. + +### A `függvény` deklaráció + + function foo() {} + +Ez a függvény felkerül a scope tetejére ([hoisting](#function.scopes)), mielőtt a kód végrehajtása megtörténne. Így abban a scope-ban, ahol *definiálták*, *mindenhol* elérhető, +még abban a trükkös esetben is, hogyha a kód azon pontján hívjuk ezt a függvényt, mielőtt +definiáltuk volna (látszólag). + + foo(); // Így is működik + function foo() {} + +### A `függvény` kifejezés (expression) + + var foo = function() {}; + +A fentebbi példában egy *névtelen* függvényt adunk értékül a foo változónak. + + foo; // 'undefined' + foo(); // TypeError hiba + var foo = function() {}; + +Habár ebben a példában a `var` deklaráció futás előtt a kód tetejére kúszik, +ettől függetlenül a foo mint függvény meghívásakor hibát fogunk kapni. + +Ugyanis a deklaráció felkúszott, azonban az értékadás csak futásidőben fog megtörténni, +addig is a foo változó értéke [undefined](#core.undefined) marad. Az undefinedot pedig hiába hívjuk függvényként, TypeErrort kapunk végeredményül. + +### Névvel ellátott függvény kifejezés + +Egy másik érdekes eset, amikor névvel ellátott függvényeket adunk értékül változóknak. + + var foo = function bar() { + bar(); // Működik + } + bar(); // ReferenceError + +Ebben a példában a `bar`t önmagában nem lehet elérni egy külső scope-ból (utolsó sor), +mivel egyből értékül adtuk a `foo` változónak. Ennek ellenére a `bar`on belül elérhető +a `bar` név. A tanulság az, hogy a függvény önmagát *mindig* eléri a saját scopeján belül, és ez a JavaScriptben található [névfeloldásnak](#function.scopes) köszönhető. + + diff --git a/external/JavaScript-Garden/doc/hu/function/scopes.md b/external/JavaScript-Garden/doc/hu/function/scopes.md new file mode 100644 index 0000000..7e75a0b --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/scopes.md @@ -0,0 +1,234 @@ +## Névterek és hatókörök + +Habár látszólag a kapcsos zárójelek jelentik a blokkok határait JavaScriptben, +fontos megjegyezni hogy **nincsen** blokk szintű hatókör, csakis *függvény hatókörök* +léteznek. + + function test() { // ez egy hatókör + for(var i = 0; i < 10; i++) { // ez meg nem + // utasítások... + } + console.log(i); // 10 + } + +> **Megjegyzés**: Amikor a `{...}` jelölés nem értékadásban, return utasításban vagy +> függvény argumentumként szerepel, akkor blokk utasításként lesz értelmezve és +> nem objektum literálként. Ez a szép tulajdonság az [automatikus pontosvessző +> generálással](#core.semicolon) karöltve nehezen észrevehető hibákhoz vezethet. + +A nyelvben nincsenek beépített névterek, ami azt jelenti hogy minden, egyetlen +*globálisan megosztott* névtérben kerül deklarálásra. + +Akárhányszor egy változóra hivatkozunk, a JavaScript elkezdi felfele utazva +megkeresni hatókörökön, amíg csak meg nem találja. Hogyha elérjük +a globális hatókört és még mindig nem találjuk a keresett változót, akkor egy +`ReferenceError` hibával gazdagodik a futásidőnk. + +### A globális változók csapása + + // A script + foo = '42'; + + // B script + var foo = '42' + +Érdemes észrevenni, hogy a fenti két scriptnek **nem** ugyanaz a hatása. Az A script +egy `foo` nevű változót vezet be a *globális* hatókörben, a B script pedig egy `foo` +nevű változót deklarál az *ő hatókörében*. + +Mégegyszer tehát, ez a kettő **nem** *ugyanazt jelenti*: a `var` elhagyásának jópár +beláthatatlan következménye is lehet. + + // globális hatókör + var foo = 42; + function test() { + // lokális hatókör + foo = 21; + } + test(); + foo; // 21 + +Itt, a `var` elhagyása azt eredményezi, hogy a `test` függvény mindig felülírja +a globális hatókörben definiált `foo` változó értékét. Habár ez elsőre nem tűnik +nagy dolognak, ha a `var`okat több száz sornyi JavaScript kódból hagyjuk el, az +olyan hibákhoz vezethet, amit még az anyósunknak se kívánnánk. + + // globális hatókör + var items = [/* random lista */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // a subLoop hatóköre + for(i = 0; i < 10; i++) { // hiányzik a var + // elképesztő dolgokat művelünk itt + } + } + +Ennél a kódnál a külső ciklus az első `subLoop` hívás után megáll, mivel a `subLoop` +felülírja az `i` változó globális értékét. Hogyha a második `for` ciklusban használtuk +volna `var`-t azzal könnyen elkerülhettük volna ezt a hibát. **Sose** hagyjuk el a `var` utasítást, ha csak nem direkt az a *kívánt hatás*, hogy befolyásoljuk a +külső hatókört. + + +### Lokális változók + +Kétféleképp (és nem több módon) lehet lokális változókat JavaScriptben leírni; ez vagy a [függvény](#function.general) paraméter vagy a `var` utasítás. + + // globális hatókör + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // a test függvény lokális hatóköre + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +Itt a `foo` és `i` lokális változók a `test` hatókörén belül, viszont a `bar`os +értékadás felül fogja írni a hasonló nevű globális változót. + +### Hoisting + +A JS **hoistolja** (megemeli) a deklarációkat. Ez azt jelenti hogy minden `var` +utasítás és `függvény` deklaráció az őt körülvevő hatókör tetejére kerül. + + 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]; + } + } + +A fenti kód átalakul egy másik formára mielőtt lefutna. A JavaScript felmozgatja +a `var` utasításokat és a `függvény` deklarációkat, az őket körülvevő legközelebbi +hatókör tetejébe. + + // a var utasítások felkerülnek ide + var bar, someValue; // alapból mindegyik 'undefined' értékű lesz + + // a függvény deklaráció is felkerül ide + function test(data) { + var goo, i, e; // ezek is felkerülnek + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // Ez TypeErrorral elszáll, mivel a bar még 'undefined' + someValue = 42; // az értékadásokat nem piszkálja a hoisting + bar = function() {}; + + test(); + +A hiányzó blokk hatókör ténye nem csak azt eredményezi, hogy a `var` utasítások +kikerülnek a ciklusmagokból, hanem az `if` utasítások kimenetele is megjósolhatatlan +lesz. + +Habár úgy látszik az eredeti kódban, hogy az `if` utasítás a `goo` *globális +változót* módosítja, a hoisting után látjuk hogy valójában a *lokális változóra* +lesz befolyással. Trükkös. + +A *hoisting* tudása nélkül valaki azt hihetné, hogy az alábbi kód egy `ReferenceError` +-t fog eredményezni. + + // nézzük meg hogy a SomeImportantThing inicializálva lett-e + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Persze ez működik, annak köszönhetően hogy a `var` utasítás a *globális hatókör* +tetejére lett mozgatva. + + var SomeImportantThing; + + // más kódok még inicializálhatják az előbbi változót itt... + + // ellenőrizzük hogy létezik-e + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Névfeloldási sorrend + +JavaScriptben az összes hatókörnek -beleértve a *globálisat* is- megvan a maga +[`this`](#function.this) változója, amelyik mindig az *aktuális objektumra* utal. + +A függvény hatókörökben van még egy speciális [`arguments`](#function.arguments) +változó is mindig definiálva, amely a függvénynek átadott argumentumokat +tartalmazza. + +Hogy hozzunk egy példát, amikor valaki a `foo` nevű változót próbálja elérni egy +függvény hatókörön belül, a JavaScript az alábbi sorrendben fogja keresni az adott +változó nevet. + + 1. Abban az esetben ha találunk `var foo` utasítást, használjuk azt. + 2. Hogyha bármelyik függvény paraméter neve `foo`, használjuk azt. + 3. Hogyha magának a függvénynek a neve `foo, használjuk azt. + 4. Menjünk a külső hatókörre, és kezdjük újra **#1**-től. + +> **Megjegyzés**: Egy `arguments` nevű függvény paraméter **megakadályozza** +> a bépített `arguments` objektum létrehozását. + +### Névterek + +Hogyha egyetlen globális névterünk van, akkor egy gyakori probléma lehet az, +hogy névütközésekbe futunk. A JavaScriptben szerencsére ez a gond könnyen +elkerülhető a *névtelen wrapper függvények* használatával. + + (function() { + // egy 'öntartalmazó' névtér + + window.foo = function() { + // egy exportált closure + }; + + })(); // a függvényt azonnal végre is hajtjuk + +A névtelen függvények [kifejezésekként](#function.general) vannak értelmezve; így +ahhoz hogy meghívhatóak legyenek, először ki kell értékelni őket. + + ( // a függvény kiértékelése a zárójeleken belül + function() {} + ) // a függvény objektum visszatérítése + () // az eredmény meghívása + +Persze más kifejezések is használhatóak arra, hogy kiértékeljük és meghívjuk +a függvény kifejezést, amelyek habár szintaxisukban eltérnek, ugyan azt eredményezik. + + // Még több stílus anonymus függvények azonnali hívásához... + !function(){}() + +function(){}() + (function(){}()); + // és a lista folytatódik... + +### Összegzésül + +Az *anonym wrapper függvények* használata erősen ajánlott a kód egységbezárása +érdekében, saját névtér alkotásához. Ez nem csak hogy megvédi a kódunkat a +névütközésektől, de jobb modularizációhoz is vezet. + +Emelett a globális változók használata **nem ajánlott**. **Bármilyen** fajta +használata rosszul megírt kódról árulkodik, amelyik könnyen eltörik és nehezen +karbantartható. diff --git a/external/JavaScript-Garden/doc/hu/function/this.md b/external/JavaScript-Garden/doc/hu/function/this.md new file mode 100644 index 0000000..8c36387 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/function/this.md @@ -0,0 +1,110 @@ +## A `this` mágikus működése + +A `this` kicsit másképp működik a JavaScriptben, mint ahogy azt megszokhattuk +más nyelvekben. Ugyanis pontosan **öt féle** módja lehet annak, hogy a `this` +éppen mire utal a nyelvben. + +### A Globális hatókör + + this; + +Amikor globális hatókörben van használva a `this`, akkor pontosan a *globális* objektumra utal. + +### Függvény híváskor + + foo(); + +Itt a `this` megint a *globális* objektumra fog utalni. + +> **ES5 Megjegyzés:** Strict módban a globális eset **nem létezik** többé. +> Ezekben az esetekben a `this` értéke undefined lesz. + +### Eljárás hívásakor + + test.foo(); + +Ebben a példában a `this` a `test` objektumra fog hivatkozni. + +### Konstuktor hívásakor + + new foo(); + +Ha a függvény hívását a `new` kulcsszóval előzzük meg, akkor a függvény [konstruktorként](#function.constructors) fog viselkedni. A függvényen belül, a `this` +az *újonnan létrehozott* `Objektumra` fog hivatkozni. + +### A `this` explicit beállítása + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // ugyanaz mint egy sorral lejjebb + foo.call(bar, 1, 2, 3); // argumentumok: a = 1, b = 2, c = 3 + +A `Function.prototype`-ban levő `call` vagy `apply` használatakor aztán elszabadul a pokol 😀. +Ezekben az esetekben ugyanis a this a foo hívásakor **egzaktan** be lesz állítva az apply/call +első argumentumára. + +Ennek eredményképp az előzőekben említett *Eljárás hívásakor* rész **nem** érvényes, +a `foo` fentebbi meghívásakor a `this` értéke a `bar` objektumra lesz beállítva. + +> **Megj.:** A `this` kulcsszót **nem lehet** `Objektum` literál létrehozásakor arra használni, +> hogy magára az objektumra hivatkozzon. +> Így a `var obj = {me: this}` kódban a `me` **nem fog** a `this`-re hivatkozni, ugyanis +> ez az eset nem tartozik egyikhez sem a fent megtalálható öt közül. + +### Gyakori buktatók + +Míg a fent megtalálható eseteknek van gyakorlatban vett értelme, az első +a nyelv rossz designjára utal, ugyanis ennek **soha** nem lesz semmilyen +praktikus felhasználási módja. + + Foo.method = function() { + function test() { + // A this itt a globális ojjektum. + } + test(); + }; + +Gyakori hiba, hogy úgy gondolják a fenti példában az emberek, hogy a `this` a `test` függvényen +belül az őt körülvevő `Foo`-ra fog mutatni, pedig **nem**. + +Megoldásképp, hogy a `Foo`-hoz hozzáférhessük a `test`-en belül, szükségszerű egy változót +lokálisan elhelyezni a `method`-on belül, ami már valóban a kívánt `this`-re (`Foo`-ra) mutat. + + Foo.method = function() { + var that = this; + function test() { + // Használjuk a that-et a this helyett + } + test(); + }; + +A `that` tuladjonképpen egy mezei változónév (nem kulcsszó), de sokszor használják arra, +hogy egy másik `this`-re hivatkozzanak vele. A [closure-ökkel](#function.closures) kombinálva +ez a módszer arra is használható, hogy `this`-eket passzolgassunk a vakvilágban és még tovább. + +### Eljárások értékül adása + +Egy másik koncepció ami **nem** fog a JavaScriptben működni, az az alias függvények létrehozása, ami tulajdonképpen egy függvény másik névhez való **kötését** jelentené. + + var test = someObject.methodTest; + test(); + + +Az első eset miatt a `test` egy sima függvényhívásként működik, azonban a `this` értéke +a függvényen belül a továbbiakban **nem** a `someObject` lesz. + +Elsőre a `this` alábbi módon való utánkötése (late binding) nem tűnik jó ötletnek. +Azonban ez az, amitől a [prototípusos öröklődés](#object.prototype) is működni tud, +ami a nyelv egyik fő erőssége. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Amikor a `method` meghívódik a `Bar` példányaként, a `this` pontosan a `Bar` +megfelelő példányára fog mutatni. diff --git a/external/JavaScript-Garden/doc/hu/index.json b/external/JavaScript-Garden/doc/hu/index.json new file mode 100644 index 0000000..c3a3c82 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "description": "Bevezet\u00E9s a JavaScript bugyraiba, hib\u00E1iba.", + "langTitle": "A JavaScript Garden magyarul", + "sections": [ + { + "title": "Bevezet\u0151", + "dir": "intro", + "articles": [ "index" ] + }, + { + "title": "Objektumok", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "F\u00FCggv\u00E9nyek", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "T\u00F6mb\u00F6k", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "T\u00EDpusok", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "L\u00E9nyeg", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Egy\u00E9b", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/hu/intro/index.md b/external/JavaScript-Garden/doc/hu/intro/index.md new file mode 100644 index 0000000..83bb9b9 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/intro/index.md @@ -0,0 +1,41 @@ +## Bevezető + +A **JavaScript Garden** egy folytonosan növekvő kódgyűjtemény amely a nyelv kihívást okozó részeit hivatott tisztázni. Itt tanácsokat kaphatsz afelől, hogyan kerüld el a leggyakoribb programozási- valamint nyelvi hibákat, hogyan írj gyorsabb kódot, és mik a legsűrűbben elkövetett *bad practice*k amelyekkel egy átlagos JavaScript programozó (nem) mindennapi útján találkozhat a nyelv megismerése közben. + +A JavaScript Gardennek **nem** célja, hogy megtanítsa a JavaScript nyelvet! +Az itt felsorolt témák megértéséhez mindenképp szükséges némi tapasztalat. Ha a nyelv alapjait szeretnéd elsajátítani, először nézd át ezt a kiváló [tutorialt][1] a Mozilla Developer Networkön. + +## Szerzők + +Ez a leírás két kiváló [Stack Overflow][2] felhasználó, [Ivo Wetzel][3] (szerző) és [Zhang Yi Jiang][4] (Design) tollából született. + +A dokumentumot jelenleg [Tim Ruffles](http://truffles.me.uk) gondozza. + +## Hozzájárultak még + + - Túl hosszú lenne itt mindenkit felsorolni, lásd a [közreműködők listáját](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + + - A magyar fordítás [jozsefDevs](https://twitter.com/jozsefDevs) jóvoltából született. + +## Hosting + +A JavaScript Garden a GitHubon van hostolva, de a [Cramer Development][7] jóvoltából +a [JavaScriptGarden.info][8] címen is elérhető. + +## Licensz + +A JavaScript Garden az [MIT licensszel][9] van ellátva és a [GitHubon][10] hostoljuk. +Ha bármilyen hibát vagy elírást veszel észre, kérlek [jelezd azt][11], vagy javítsd és küldj egy pull requestet. Továbbá, megtalálhatsz minket a [JavasScript szobában][12] a Stack Overflow chaten. + +[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/external/JavaScript-Garden/doc/hu/object/forinloop.md b/external/JavaScript-Garden/doc/hu/object/forinloop.md new file mode 100644 index 0000000..642dc26 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/object/forinloop.md @@ -0,0 +1,53 @@ +## A `for in` ciklus + +Csak úgy, mint a jó öreg `in` operátor, a `for in` is bejárja az egész +prototípus láncot, amikor egy objektum mezőin próbálnánk iterálni. + +> **Megjegyzés:** A `for in` ciklus **nem** fog iterálni azokon a mezőkön, +> amelyeknek az `enumerable` tulajdonsága `false`-ra van állítva. Például a +> `length` mező nem kerül az iterációba amikor egy tömbön iterálnánk végig. + + // Mérgezzük Object.prototype-ot! + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // mind a moo és bar is kiírásra kerül + } + +Mivel -hála égnek- magának a `for in` ciklusnak a működését nem lehet befolyásolni, +így más módszert kell találnunk ahhoz hogy száműzzük a váratlan mezőket a ciklus magból. +(Értsd: Azokat amelyek a prototípus láncon csücsülnek csak). Ezt pedig az `Object.prototype`-ban +lakó [`hasOwnProperty`](#object.hasownproperty) függvény használatával érhetjük el. + +> **Fontoljuk meg:** Mivel a `for in` mindig bejárja a teljes prototípus láncot, +> így minnél több elemet adunk a származtatási láncunkba, annál lassabban fog tekerni. + +### Szűrés használata a `hasOwnProperty`-vel + + // még mindig a fenti foo-nál tartunk + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Ez az egyetlen helyes útja annak, hogy az objektum saját mezőin iteráljunk csak végig. +Mivel a `hasOwnProperty`-t használjuk, így csak a várt `moo`-t fogja kiírni. Tehén jó +kódunk van! Hogyha a `hasOwnProperty`-t kihagynánk, a kódunk ki lenne téve nem várt +hibáknak, amik pl. abból fakadnak, hogy valaki ocsmányul kiterjesztette az +`Object.prototype`-t. + +Például, ha a [Prototype][1] frameworköt használjuk, és nem ilyen stílusban írjuk a +ciklusainkat, a hibák szinte garantáltak, ugyanis ők saját szájízükre kiterjesztik az +`Object.prototype`-t. + +### Konklúzió + +A `hasOwnProperty` használata erősen javasolt. Soha ne éljünk pozitív +feltételezésekkel a futó kódot illetően, főleg olyan döntésekben nem érdemes +orosz rulettezni, mint hogy kiterjeszti-e valaki a natív prototípusokat vagy nem. +Mert általában igen. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/hu/object/general.md b/external/JavaScript-Garden/doc/hu/object/general.md new file mode 100644 index 0000000..0032e73 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/object/general.md @@ -0,0 +1,90 @@ +## Objektumok és mezők használata + +A JavaScriptben minden objektumként működik, a [`null`](#core.undefined) és az [`undefined`](#core.undefined) kivételével. + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Gyakori tévhitként terjed, hogy a JavaScriptben a számok nem használhatóak objektumként. +Ez csak látszólag igaz, mivel a JavaScript a pont utáni részt úgy próbálja értelmezni, +mintha lebegőpontos számot látna. Így hibát kaphatunk. + + 2.toString(); // SyntaxErrort vált ki + +Azonban számos kifejezés létezik megoldásként, amelyekkel megkerülhető ez a probléma. + + 2..toString(); // így a második pont már az objektumra utal + 2 .toString(); // fontos a space-t észrevenni itt a pont előtt + (2).toString(); // a 2 értékelődik ki hamarabb + +### Objektumok mint adattípusok + +Az objektumok JavaScriptben [*Hash táblaként*][1] is használhatóak, mivel természetszerűleg kulcs-érték párokat tartalmaznak. + +Az objektum literál leírásával - `{}` jelöléssel - lehet létrehozni egy új objektumot. Ez az új objektum az `Object.prototype`-ból [származik](#object.prototype) és nincsenek [saját mezői](#object.hasownproperty) definiálva. + + var foo = {}; // egy új, üres objektum + + // egy új objektum egy 'test' nevű mezővel, aminek 12 az értéke + var bar = {test: 12}; + +### Mezők elérése + +Egy objektum mezői kétféle módon érhetőek el, vagy az 'objektum.mezőnév' jelöléssel, +(Ford.: amit "dot notationként" emlegetünk) vagy a szögletes zárójelek kirakásával. + + var foo = {name: 'macska'} + foo.name; // macska + foo['name']; // macska + + var get = 'name'; + foo[get]; // macska + + foo.1234; // SyntaxError + foo['1234']; // működik + +A két jelölés majdnem egyenértékűen használható, kivéve, hogy a szögletes zárójelekkel dinamikusan állíthatunk be mezőket és olyan neveket is választhatunk, amik amúgy szintaxis hibához vezetnének (Fordító: mivel a neveket stringbe kell rakni, megadhatunk a JS által "lefoglalt" kulcsszavakat is mezőnévként, habár ennek használata erősen kerülendő). + +### Mezők törlése + +Egyetlen módon lehet mezőt törölni egy objektumból, ez pedig a `delete` operátor +használata; a mező értékének `undefined`-ra vagy `null`-ra való állítása csak +magára az értékre van kihatással, de a kulcs ugyanúgy megmarad az objektumban. + + 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]); + } + } + +A fenti ciklus a `bar undefined` és a `foo null` eredményeket fogja kiírni - +egyedül a `baz` mező került törlésre, és emiatt hiányzik is az outputról. + +### Kulcsok jelölése + + var test = { + 'case': 'Kulcsszó vagyok, ezért stringként kell leírnod', + delete: 'Én is az vagyok' // SyntaxError + }; + +Az objektumok mezőnevei mind stringként, mind egyszerű szövegként (Ford.: aposztrófok nélkül) +leírhatóak. A JavaScript értelmező hibája miatt, a fenti kód azonban `SyntaxErrort` eredményez ECMAScript 5 előtti verzió esetén. + +Ez a hiba onnan ered, hogy a `delete` egy *kulcsszó*, viszont érdemes *string literálként* +leírni, hogy helyesen megértsék a régebbi JavaScript motorok is. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/hu/object/hasownproperty.md b/external/JavaScript-Garden/doc/hu/object/hasownproperty.md new file mode 100644 index 0000000..245a328 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/object/hasownproperty.md @@ -0,0 +1,61 @@ +## `hasOwnProperty` + +Hogy meg tudjuk nézni egy adott objektum saját mezőit - azokat a mezőket, amelyek +az objektumon *közvetlenül* vannak definiálva, és nem valahol a +[prototípus láncon](#object.prototype) -, a `hasOwnProperty` függvény használata +ajánlott, amelyet az összes objektum amúgy is örököl az `Object.prototype`-ból. + +> **Megj.:** Vicces programozók miatt, **nem** biztos hogy elég lesz megnézni hogy +> egy adott mező `undefined`-e. Mivel lehet hogy ekkor maga a mező létezik, csak valaki +> konkrétan az értékét `undefined`-ra állította. + +A `hasOwnProperty` függvény az egyetlen olyan dolog amelyik anélkül tudja ellenőrizni +az objektum mezőit, hogy megpróbálná bejárni a prototípus láncot. + + // Az Object.prototype beszennyezése + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // igaz + + foo.hasOwnProperty('bar'); // hamis + foo.hasOwnProperty('goo'); // igaz + +Hogy megértsük a fontosságát, egyedül a `hasOwnProperty` tudja hozni a korrekt +és elvárt eredményeket mezőellenőrzés szempontjából. Egyszerűen **nincs más** +módja annak, hogy kizárjuk a szűrésünkből azokat a mezőket amelyek nem az objektumon, +hanem valahol feljebb, a prototípus láncon lettek definiálva. + +### A `hasOwnProperty` mint mező + +A JavaScript persze nem védi magát a `hasOwnProperty` nevet, így egy jókedvű +programozóban mindig megvan a lehetőség, hogy így nevezze el a saját függvényét. +Ennek kikerülése érdekében ajánlott mindig a `hasOwnProperty`-re *kívülről* hivatkozni +(Értsd: A hackelt -saját hasOwnPropertyvel ellátott- objektum kontextusán kívüli objektum hasOwnPropertyjét hívjuk meg). + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Mordor itt kezdődik' + }; + + foo.hasOwnProperty('bar'); // mindig hamissal tér vissza + + // Használhatjuk egy másik objektum hasOwnPropertyjét, + // hogy meghívjuk a foo-n. + ({}).hasOwnProperty.call(foo, 'bar'); // ez már igaz + + // Szintén jó megoldás lehet közvetlenül az + // Object prototypejából hívni ezt a függvényt. + Object.prototype.hasOwnProperty.call(foo, 'bar'); // ez is igaz + + +### Konklúzió + +A `hasOwnProperty` használata az **egyetlen** megbízható módszer annak eldöntésére, +hogy egy mező közvetlenül az objektumon lett-e létrehozva. Melegen ajánlott a +`hasOwnProperty`-t **minden** [`for in` ciklusban](#object.forinloop) használni. +Használatával ugyanis elkerülhetjük a kontár módon kiegészített natív prototípusokból +fakadó esetleges hibákat, amire az imént láttunk példát. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/object/prototype.md b/external/JavaScript-Garden/doc/hu/object/prototype.md new file mode 100644 index 0000000..27a533b --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/object/prototype.md @@ -0,0 +1,120 @@ +## A Prototípus + +A JavaScript nem a klasszikus öröklődést használja, hanem egy ún. *prototípusos* +származtatást használ. + +Míg ezt gyakran a JavaScript legnagyobb hibái között tartják számon, valójában +ez a származtatási modell jóval kifejezőbb, mint klasszikus barátja. +Ezt jelzi, hogy például sokkal könnyebb megépíteni a klasszikus modellt, alapul véve +a prototípusos modellt, míg a fordított irány kivitelezése igencsak nehézkes lenne. + +A JavaScript az egyetlen széles körben elterjedt nyelv, amely ezt a származtatást +használja, így mindenképp időt kell szánni a két modell közti különbség megértésére. + +Az első feltűnő különbség, hogy ez a fajta származtatás *prototípus láncokat* +használ. + +> **Megj.:** Egyszerűen a `Bar.prototype = Foo.prototype` utasítást használva, mind a +> két objektum **ugyanazon** a prototípus objektumon fog osztozni. Így aztán ha bárki közülük +> megváltoztatja ezt a prototípust, az a változás a másik objektum működését is befolyásolja, +> ami általában nem egyezik meg a kívánt működéssel. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Beállítjuk a Bar prototípusát a Foo egy új példányára + Bar.prototype = new Foo(); // ! + Bar.prototype.foo = 'Hello World'; + + // Beállítjuk a Bar konstruktorát + Bar.prototype.constructor = Bar; + + var test = new Bar(); // új Bar példány létrehozása + + // A kapott prototípus lánc + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* stb. */ } + +A fenti kódban a `test` objektum mind a `Bar.prototype` és `Foo.prototype` +prototípusokból származik, így lesz hozzáférése a `method` nevű függvényhez amely +a `Foo` prototípusában lett definiálva. A `value` mezőhöz szintén lesz hozzáférése, +amely akkor jött létre, amikor (szám szerint) **egy** új `Foo` példányt hoztunk létre. +Érdemes észrevenni hogy a `new Bar()` kifejezés **nem** hoz létre egy új `Foo` példányt +minden alkalommal, azonban újrahasználja azt az egyetlen (//!) inicilalizált `Foo` pédlányunkat. Így az összes `Bar` példány *egy és ugyanazt* a `value` mezőt (és +értéket) fogja használni. + +> **Megj.:** **Ne** használd a `Bar.prototype = Foo` kifejezést, mivel ez nem +> a `Foo` prototípusára fog mutatni, hanem magára a `Foo` függvényre, mint objektumra. +> Így a prototípus lánc a `Function.prototype`-ra fog futni a `Foo.prototype` helyett. +> Ekkor, a `method` függvény nem lesz benne a prototípus láncban. + +### Mezők keresése + +Amikor olyan utasítást adunk ki, amellyel egy objektum mezőjét keressük, a +JavaScript **felfele** bejárja az egész prototípus láncot, amíg meg nem találja +a kért mezőt. + +Hogyha eléri a lánc legtetejét - nevezetesen az `Object.prototype`-t és még +ekkor sem találja a kért mezőt, akkor az [undefined-dal](#core.undefined) fog +visszatérni. + +### A Prototype mező + +Alapjáraton, a JavaScript a prototype nevű mezőt használja a prototípus láncok +kialakításához, de ettől függetlenül ez is ugyanolyan mező mint a többi, és +**bármilyen** értéket be lehet neki állítani. Viszont a primitív típusokat egyszerűen +figyelmen kívül fogja hagyni a feldolgozó. + + function Foo() {} + Foo.prototype = 1; // nincs hatása + +Az objektumok megadása, mint azt a fentebbi példában láthattuk, hatással van a prototype +mezőkre és ezeknek az átállításával bele lehet szólni a prototípus láncok kialakításába. + +### Teljesítmény + +Értelemszerűen, minél nagyobb a prototípus lánc, annál tovább tart egy-egy mező +felkeresése, és ez rossz hatással lehet a kód teljesítményére. Emellett, ha egy +olyan mezőt próbálunk elérni, amely nincs az adott objektum példányban, az mindig +a teljes lánc bejárását fogja eredményezni. + +Vigyázat! Akkor is bejárjuk a teljes láncot, amikor egy objektum mezőin próbálunk [iterálni](#object.forinloop). + +### Natív prototípusok bővítése + +Egy gyakran elkövetett hiba, hogy az `Object.prototype` prototípust vagy egy másik előre +definiált prototípust próbálunk kiegészíteni új kóddal. + +Ezt [monkey patching][1]-nek is hívják, és aktívan kerülendő, mivel megtöri +az *egységbe zárás* elvét. Habár ezt a technikát olyan népszerű framework-ök +is használják, mint a [Prototype][2], ettől függetlenül ne hagyjuk magunkat csőbe húzni; +nincs ésszerű indok arra, hogy összezavarjuk a beépített típusokat, további +*nem standard* saját funkcionalitással. + +Az **egyetlen** ésszerű használati indok a natív prototípusokba nyúlásra az lehet, +hogy megpróbáljuk szimulálni az új JavaScript motorok szolgáltatásait régebbi társaikon, például az [`Array.forEach`][3] implementálásával. + +### Zárásként + +**Nagyon fontos** megérteni a prototípusos származtatási modellt, mielőtt olyan +kódot próbálnánk írni, amely megpróbálja kihasználni a sajátosságait. Nagyon +oda kell figyelni a prototípuslánc hosszára - osszuk fel több kis láncra ha +szükséges - hogy elkerüljük a teljesítmény problémákat. Továbbá, a natív +prototípusokat **soha** ne egészítsük ki, egészen addig amíg nem akarunk +JavaScript motorok közötti kompatibilitási problémákat áthidalni. + +[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/external/JavaScript-Garden/doc/hu/other/timeouts.md b/external/JavaScript-Garden/doc/hu/other/timeouts.md new file mode 100644 index 0000000..7ad3766 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/other/timeouts.md @@ -0,0 +1,167 @@ +### A varázslatos `setTimeout` és `setInterval` + +Mivel a JavaScript aszinkron, a `setTimeout` és `setInterval` használatával +lehetséges késleltetni a kódok lefutási idejét. + +> **Megjegyzés:** A timeout függvények **nem** részei az ECMAScript Standardnek. +> Mivel a [DOM][1] részeként lettek implementálva. + + function foo() {} + var id = setTimeout(foo, 1000); // Egy számmal (> 0) tér vissza + +Amikor a `setTimeout` függvényt meghívjuk, válaszul egy timeout ID-t kapunk, +valamint be lesz ütemezve a `foo` függvényhívás, hogy **körülbelül** 1000 miliszekundum múlva fusson le a jövőben. A `foo` **egyszer** lesz végrehajtva. + +Az aktuális JavaScript motor időzítésétől függően, és annak figyelembe vételével, +hogy a JavaScript mindig egyszálú, tehát a megelőző kódok blokkolhatják a szálat, +**soha** nem lehet biztonságosan meghatározni hogy valóban a kért időzítéssel +fog lefutni a kód amit megadtunk a `setTimeout`ban. Erre semmilyen biztosíték nincs. + +Az első helyen bepasszolt függvény a *globális objektum* által lesz meghívva, ami +azt jelenti hogy a [`this`](#function.this) a függvényen belül a globális objektumra +utal. + + function Foo() { + this.value = 42; + this.method = function() { + // a this egy globális objektumra utal, nem a Foo-ra + console.log(this.value); // undefined-ot logol ki + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Note:** Mivel a `setTimeout` egy **függvény objektumot** vár első paramétereként +> egy gyakori hiba a `setTimeout(foo(), 1000)` módon való használata, amely a +> `foo` **visszatérési értékét** fogja használni és **nem** a `foo`t mint függvényt. +> Ez a legtöbb esetben egy észrevétlen hibát okoz, mivel a függvény `undefined`-t +> térít vissza amire a `setTimeout` **nem** fog hibát dobni. + +### Híváshalmozás a `setInterval`al + +Míg a `setTimeout` csak egyszer futtatja le a megadott függvényt, a `setInterval` - +ahogy a neve is mutatja - **minden** `X` miliszekundumban végrehajtja a +neki átadott kódot, használata pedig erősen kerülendő. + +Nagy hátulütője, hogy még akkor is ütemezi az újabb és újabb +hívásokat, hogyha az aktuálisan futattot kód a megadott időintervallumon +felül blokkolja a további kód futtatást. Ez, hogyha megfelelően rövid +intervallumokat állítunk be, felhalmozza a függvényhívásokat a call stacken. + + function foo(){ + // kód ami 1 másodpercig feltartja a futtatást + } + setInterval(foo, 100); + +A fenti kódban amikor a `foo` meghívódik, 1 másodpercig feltartja a további futtatást. + +A `setInterval` persze ütemezni fogja a jövőbeli `foo` hívásokat továbbra is, amíg +blokkolódik a futtatás. Így **tíz** további hívás fog várakozni, miután a `foo` +futtatása először végzett. + +### Hogyan Bánjunk El a Blokkolással + +A legkönnyebb és kontrollálhatóbb megoldásnak az bizonyul, ha a `setTimeout` +függvényt a rögtön a foo-n belül használjuk. + + function foo(){ + // 1 másodpercig blokkoló kód + setTimeout(foo, 100); + } + foo(); + +Ez nem csak egységbe zárja a `setTimeout` hívást, de meggátolja a felesleges hívások +felhalmozását, és több irányítást ad a kezünkbe. A `foo` így magától el tudja +dönteni, hogy akarja-e újra futtatni önmagát vagy sem. + +### Timeout Tisztogatás Kézzel + +A `clearTimeout` vagy `clearInterval` hívással tudjuk a timeoutjainkat +megszüntetni, természetesen attól függ, hogy melyiket használjuk, +hogy melyik `set` függvénnyel indítottuk útjára a timeoutunkat. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Az Összes Timeout Megszüntetése + +Mivel nincsen beépített megoldás az összes timeout és/vagy interval +hívás törlésére, ezért bruteforce módszerekhez kell folyamodjunk. + + // az "összes" timeout kitörlése + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Persze ez csak véletlenszerű lövöldözés, semmi sem garantálja, hogy a fenti +módszerrel nem marad timeout a rendszerben (A ford.: például az ezredik timeout vagy +afelett). Szóval egy másik módszer ennek megoldására, hogy feltételezzük hogy +minden `setTimeout` hívással az azonosítók száma egyel növekszik. + + // az "összes" timeout kiírtása + var legnagyobbTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= legnagyobbTimeoutId; i++) { + clearTimeout(i); + } + +Habár ez a megoldás minden böngészőben megy (egyelőre), ez az azonosítókról született mondás nincs specifikációban rögzítve, és ennek megfelelően változhat. +Az ajánlott módszer továbbra is az, hogy kövessük nyomon az összes timeout azonosítót, amit generáltunk, és így ki is tudjuk őket rendesen törölni. + +### `eval` A Színfalak Mögött + +Habár a `setTimeout` és a `setInterval` (kód) stringet is tud első paramétereként +fogdani, ezt a fajta formáját használni kimondottan **tilos**, mivel a függöny +mögött ő is csak `eval`t használ. + +> **Megjegyzés:** Mivel az ECMAScript Standard nem specifikálja a timeout +> függvények működését, az eltérő JavaScript implementációkban eltérő módon +> működhetnek. Például a Microsoft JScript-je a `Function` konstruktort használja +> az `eval` helyett. + + function foo() { + // meg lesz hívva + } + + function bar() { + function foo() { + // soha nem hívódik meg + } + setTimeout('foo()', 1000); + } + bar(); + +Mivel az `eval`t nem [direkt](#core.eval) módon hívjuk meg a fenti esetben, +a `setTimeout`nak passzolt string a *globális hatókörben* fog lefutni; így +a lokális `foo` függvényt sosem használjuk a `bar` hatóköréből. + +Továbbá **nem** ajánlott argumentumokat átadni annak a függvénynek amelyik +a timeout függvények által meg lesz hívva a későbbiekben. + + function foo(a, b, c) {} + + // SOHA ne használd így! + setTimeout('foo(1, 2, 3)', 1000) + + // Ehelyett csomagoljuk névtelen függvénybe + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Megjegyzés:** Habár lehetséges a `setTimeout(foo, 1000, 1, 2, 3)` szintaxist +> használni, mégsem ajánlott, mivel [metódusok](#function.this) használatakor +> észrevehetetlen hibákhoz vezethet. + +### Összegzésképp + +**Soha** ne használjunk stringeket a `setTimeout` vagy `setInterval` első +paramétereiként. Ha argumentumokat kell átadni a meghívandó függvénynek, az +egyértelműen **rossz** kódra utal. Ebben az esetben a függvényhívás +lebonyolításához egy *anonim* függvény használata ajánlott. + +Továbbá, mivel az ütemező kódja nem blokkolódik a JavaScript futás által, a +`setInterval` használata úgy általában kerülendő. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/hu/types/casting.md b/external/JavaScript-Garden/doc/hu/types/casting.md new file mode 100644 index 0000000..ee60441 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/types/casting.md @@ -0,0 +1,68 @@ +## Típus kasztolás + +Előre kössük le, hogy a JavaScript egy *gyengén típusos* nyelv, így **ahol +csak tud**, ott *típus kényszerítést* használ. + + // Ezek igazak + new Number(10) == 10; // A Number.toString() számmá lesz + // visszaalakítva + + 10 == '10'; // A Stringek visszaalakulnak számmá + 10 == '+10 '; // Mégtöbb string varázslat + 10 == '010'; // és mégtöbb + isNaN(null) == false; // a null varázslatosan 0-vá alakul + // ami persze nem NaN + + // Ezek hamisak + 10 == 010; + 10 == '-10'; + +> **ES5 Megjegyzés:** A `0`-val kezdődő számliterálok oktálok (8-as számrendszer). +> Az oktál támogatást az ECMAScript 5 strict módból **eltávolították** + +Hogy elkerüljük a fenti varázslatokat, a [szigorú egyenlőség ellenőrzés](#types.equality) **melegen** ajánlott. Habár ezzel elkerüljük +a problémák farkasrészét, még mindig tartogat a JS gyengén típusos rendszere +meglepetéseket. + +### Natív típusok konstruktorai + +A jó hír az, hogy a natív típusok, mint a `Number` és a `String` különféle +módon viselkednek, hogy ha a `new` kulcsszóval avagy anélkül vannak inicializálva. + + new Number(10) === 10; // Hamis, Objektum vs. Szám + Number(10) === 10; // Igaz, Szám vs. szám + new Number(10) + 0 === 10; // Igaz, az implicit konverziónak hála + +Ha egy natív típust, mint a `Number` konstruktorként kezelünk, akkor egy új +`Number` objektumot kapunk. De ha kihagyjuk a `new` kulcsszót akkor a `Number` +egy egyszerű konverter függvényként fog viselkedni. + +Ráadásul a literálok passzolgatásakor még több típuskonverzió üti fel a fejét. + +A legjobb megoldás hogyha a három típus valamelyikére **expliciten** kasztolunk. + +### Stringre kasztolás + + '' + 10 === '10'; // igaz + +Egy üres string hozzáfűzésével könnyen tudunk egy értéket stringgé kasztolni. + +### Számra kaszt + + +'10' === 10; // igaz + +Az **unáris** plusz operátor használatával lehetséges egy értéket számra alakítani. + +### Booleanre kasztolás + +A **nem** operátor kétszeri alkalmazásával tudunk booleanné kasztolni. + + !!'foo'; // igaz + !!''; // hamis + !!'0'; // igaz + !!'1'; // igaz + !!'-1' // igaz + !!{}; // igaz + !!true; // igaz + + diff --git a/external/JavaScript-Garden/doc/hu/types/equality.md b/external/JavaScript-Garden/doc/hu/types/equality.md new file mode 100644 index 0000000..d627e86 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/types/equality.md @@ -0,0 +1,77 @@ +## Egyenlőség vizsgálat + +A JavaScriptben két különböző megoldás létezik az objektumok egyenlőségének +vizsgálatára + +### Az egyenlőség operátor + +Az egyenlőség vizsgálatot végző (egyik) operátort így jelöljük: `==` + +A JavaScript egy *gyengén típusos* nyelv. Ez azt jelenti, hogy az egyenlőség +operátor **típuskényszerítést** alkalmaz ahhoz, hogy össze tudjon hasonlítani +két értéket. + + "" == "0" // hamis + 0 == "" // igaz + 0 == "0" // igaz + false == "false" // hamis + false == "0" // igaz + false == undefined // hamis + false == null // hamis + null == undefined // igaz + " \t\r\n" == 0 // igaz + +A fenti táblázat szépen mutatja, hogy mi a típuskényszerítés eredménye, és egyben +azt is, hogy miért rossz szokás a `==` használata. Szokás szerint, ez megint +olyan fícsör, ami nehezen követhető kódhoz vezethet a komplikált konverziós +szabályai miatt. + +Pláne, hogy a kényszerítés teljesítmény problémákhoz is vezet; ugyanis, mielőtt +egy stringet egy számhoz hasonlítanánk azelőtt a karakterláncot át kell konvertálni +a megfelelő típusra. + +### A szigorú(bb) egyenlőség operátor + +Ez az operátor már **három** egyenlőségjelből áll: `===`. + +Ugyanúgy működik, mint az előbbi, kivéve hogy ez a változat **nem** alkalmaz +típuskényszerítést az operandusai között. + + "" === "0" // hamis + 0 === "" // hamis + 0 === "0" // hamis + false === "false" // hamis + false === "0" // hamis + false === undefined // hamis + false === null // hamis + null === undefined // hamis + " \t\r\n" === 0 // hamis + +A felső eredmények sokkal egyértelműbbek és ennek köszönhetően sokkal hamarabb +eltörik a kód egy-egy ellenőrzésen. Ettől sokkal hibatűrőbb lesz +a produktumunk, ráadásul teljesítménybeli gondjaink sem lesznek. + +### Objektumok összehasonlítása + +Habár mind a `==`-t és a `===`-t is egyenlőség operátornak hívjuk, eltérően +viselkednek, hogy ha legalább az egyik operandusuk egy objektum. + + {} === {}; // hamis + new String('foo') === 'foo'; // hamis + new Number(10) === 10; // hamis + var foo = {}; + foo === foo; // igaz + +Ebben az esetben mindkét operátor **identitást** és **nem** egyenlőséget +ellenőriz; tehát azt fogják ellenőrizni hogy az operandus két oldalán +ugyanaz az objektum referencia áll-e, mint az `is` operátor Pythonban +vagy a pointerek összehasonlítása C-ben. (A ford.: Tehát nem azt, hogy a +két oldalon álló objektumnak például ugyanazok-e a mezői, hanem azt hogy ugyanazon +a memóriacímen található-e a két operandus). + +### Összegzésül + +Azt érdemes tehát megjegyezni, hogy a **szigorú egyenlőség vizsgálatot** érdemes +mindig használni. Amikor szeretnék típuskényszerítést alkalmazni, akkor azt +inkább tegyük meg [direkt módon](#types.casting), és ne a nyelv komplikált +automatikus szabályaira bízzuk magunkat. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/types/instanceof.md b/external/JavaScript-Garden/doc/hu/types/instanceof.md new file mode 100644 index 0000000..50dbd48 --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/types/instanceof.md @@ -0,0 +1,35 @@ +## Az `instanceof` operátor + +Az `instanceof` operátor a két operandusának konstruktorait hasonlítja össze. +Csak akkor bizonyul hasznosnak, amikor saját készítésű objektumokon alkalmazzuk. +Beépített típusokon ugyanolyan hasztalan alkalmazni, mint a [typeof operátort](#types.typeof). + +### Saját objektumok összehasonlítása + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // igaz + new Bar() instanceof Foo; // igaz + + // Ez csak a Bar.prototypeot beállítja a Foo fv. objektumra, + // de nem egy kimondott Foo példányra + Bar.prototype = Foo; + new Bar() instanceof Foo; // hamis + +### Az `instanceof` reakciója natív típusokra + + new String('foo') instanceof String; // igaz + new String('foo') instanceof Object; // igaz + + 'foo' instanceof String; // hamis + 'foo' instanceof Object; // hamis + +Érdemes itt megjegyezni hogy az `instanceof` nem működik olyan objektumokon, +amelyek különböző JavaScript kontextusokból származnak (pl. különböző dokumentumok +a böngészőn belül), mivel a konstruktoruk nem pontosan ugyanaz az objektum lesz. + +### Összegzésül + +Az `instanceof`-ot tehát **csak** megegyező JS kontextusból származó, saját készítésű objektumoknál használjuk. Minden más felhasználása kerülendő, csak úgy, mint a [`typeof`](#types.typeof) operátor esetén. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/hu/types/typeof.md b/external/JavaScript-Garden/doc/hu/types/typeof.md new file mode 100644 index 0000000..556543d --- /dev/null +++ b/external/JavaScript-Garden/doc/hu/types/typeof.md @@ -0,0 +1,15 @@ +## A `typeof` vizsgálat + +A `typeof` operátor (az [`instanceof`](#types.instanceof)-al karöltve) +lehetőség szerint a JavaScript nyelv egyik legnagyobb buktatója, mivel majdnem +teljesen **rosszul működik**. + +Habár az `instanceof`-nak korlátozottan még lehet értelme, a `typeof` operátor +tényleg csak egyetlen praktikus use case-el rendelkezik és ez **nem** az, hogy egy +objektum típusvizsgálatát elvégezzük. + +> **Megjegyzés:** Mivel a `typeof` vizsgálatot ravaszul úgy is le lehet írni, +> mintha egy függvény lenne; `typeof(obj)`, itt jegyezzük meg hogy ez nem +> egy függvényhívás. A zárójelek ebben a kifejezésben úgy működnek mint általában, +> kiértékelik az obj változót és visszaadják az értékét. Ez pedig bekerül a +> `typeof` operandusaként. **Nincsen** `typeof` függvény. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/it/array/constructor.md b/external/JavaScript-Garden/doc/it/array/constructor.md new file mode 100644 index 0000000..c6c2edb --- /dev/null +++ b/external/JavaScript-Garden/doc/it/array/constructor.md @@ -0,0 +1,34 @@ +## Il costruttore `Array` + +Dato che il costruttore `Array` è ambiguo riguardo a come esso gestisca i suoi +parametri, si consiglia calorosamente di usare l'array letterale (notazione `[]`) +quando si creano array. + + [1, 2, 3]; // Risultato: [1, 2, 3] + new Array(1, 2, 3); // Risultato: [1, 2, 3] + + [3]; // Risultato: [3] + new Array(3); // Risultato: [] + new Array('3') // Risultato: ['3'] + +Nei casi in cui c'è solo un argomento passato al costruttore `Array` e quando +l'argomento è un `Number`, il costruttore ritornerà un nuovo array *frammentato* +con la proprietà `length` impostata al valore dell'argomento. Si noti +che in questo modo **solo** la proprietà `length` del nuovo array verrà impostata, +mentre gli indici dell'array non verranno inizializzati. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, l'indice non è stato impostato + +Essere in grado di impostare la lunghezza dell'array in anticipo è utile soltanto +in poche situazioni, come ad esempio la ripetizione di una stringa, nel cui caso +si eviterebbe l'uso di un ciclo. + + new Array(count + 1).join(stringToRepeat); + +### In conclusione + +I letterali sono da preferirsi al costruttore Array. Sono più concisi, hanno una +sintassi più chiara ed incrementano la leggibilità del codice. + diff --git a/external/JavaScript-Garden/doc/it/array/general.md b/external/JavaScript-Garden/doc/it/array/general.md new file mode 100644 index 0000000..453dc08 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/array/general.md @@ -0,0 +1,61 @@ +## Iterazione e proprietà degli Array + +Sebbene gli array in JavaScript siano oggetti, non ci sono valide ragioni +per usare il ciclo [`for in`](#object.forinloop). Infatti, ci sono varie +buone ragioni per **evitare** l'utilizzo di `for in` con gli array. + +> **Nota:** gli array in JavaScript **non** sono *array associativi*. JavaScript +> ha solo [oggetti](#object.general) per mappare chiavi con valori. E mentre +> gli array **preservano** il loro ordine, gli oggetti **non lo fanno**. + +Dato che il ciclo `for in` enumera tutte le proprietà che sono presenti nella +catena di prototipi, e dal momento che il solo modo per escludere queste +proprietà è quello di usare [`hasOwnProperty`](#object.hasownproperty), +esso è già **venti volte** più lento di un normale ciclo `for`. + +### Iterazione + +Per poter ottenere la miglior performance durante l'iterazione degli array, +è meglio usare il classico ciclo `for`. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +In questo esempio c'è un ulteriore particolare da notare, che è il caching +della lunghezza dell'array tramite `l = list.length`. + +Sebbene la proprietà `length` sia definita nell'array stesso, c'è ancora un +sovraccarico di lavoro dato dal fatto che deve essere ricercata ad ogni +iterazione del ciclo. E mentre i motori JavaScript recenti **potrebbero** +applicare delle ottimizzazioni in questo caso, non c'è modo di dire se il +codice verrà eseguito su uno di questi nuovi motori oppure no. + +Infatti, l'omissione della parte di caching può risultare in un ciclo eseguito +soltanto alla **metà della velocità** con cui potrebbe essere eseguito facendo +il caching della lunghezza. + +### La proprietà `length` + +Mentre il *getter* della proprietà `length` ritorna semplicemente il numero di +elementi che sono contenuti nell'array, il *setter* può essere usato per +**troncare** l'array. + + var arr = [1, 2, 3, 4, 5, 6]; + arr.length = 3; + arr; // [1, 2, 3] + + arr.length = 6; + arr.push(4); + arr; // [1, 2, 3, undefined, undefined, undefined, 4] + +Assegnando una lunghezza più piccola si tronca l'array. Incrementandola si +crea un array frammentato. + +### In conclusione + +Per la miglior performance, si raccomanda di usare sempre il ciclo `for` +classico e fare il caching della proprietà `length`. L'uso di `for in` su di +un array è segno di un codice scritto male che è suscettibile a bug e pessima +performance. diff --git a/external/JavaScript-Garden/doc/it/core/delete.md b/external/JavaScript-Garden/doc/it/core/delete.md new file mode 100644 index 0000000..ba4ec77 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/core/delete.md @@ -0,0 +1,90 @@ +## L'operatore `delete` + +In breve, è *impossibile* eliminare variabili globali, funzioni e qualche +altra cosa in JavaScript che ha l'attributo `DontDelete` impostato. + +### Codice globale e codice funzione + +Quando una variabile o una funzione viene definita in un scope globale o +[funzione](#function.scopes), essa è una proprietà dell'oggetto Activation +o dell'oggetto Global. Queste proprietà hanno un set di attributi, tra i quali +`DontDelete`. Dichiarazioni di variabile o funzione nel codice globale o +funzione, creano sempre proprietà con `DontDelete`, e quindi non possono essere +eliminate. + + // variabile globale: + var a = 1; // DontDelete è impostato + delete a; // false + a; // 1 + + // funzione normale: + function f() {} // DontDelete è impostato + delete f; // false + typeof f; // "function" + + // la riassegnazione non aiuta: + f = 1; + delete f; // false + f; // 1 + +### Proprietà esplicite + +Proprietà esplicitamente impostate possono essere eliminate normalmente. + + // proprietà impostata esplicitamente: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +Nel codice qui sopra, `obj.x` e `obj.y` possono essere eliminate perché +non hanno l'attributo `DontDelete`. Ecco perché anche l'esempio seguente +funziona. + + // questo funziona, tranne che per IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - solo una variabile globale + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Qui usiamo un trucco per eliminare `a`. [`this`](#function.this) qui fa +riferimento all'oggetto Global e noi dichiariamo esplicitamente la +variabile `a` come sua proprietà, il che ci permette di eliminarla. + +IE (almeno 6-8) ha alcuni bug, quindi il codice precedente non funziona. + +### Argomenti funzione e proprietà interne + +Anche i normali argomenti delle funzioni, gli +[oggetti `arguments`](#function.arguments) e le proprietà interne hanno +`DontDelete` impostato. + + // argomenti funzione e proprietà: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### Oggetti non nativi (host) + +Il comportamento dell'operatore `delete` può essere inaspettato con gli oggetti +non nativi. A causa delle specifiche, agli oggetti non nativi è permesso di +implementare qualsiasi tipo di funzionalità. + +### In conclusione + +L'operatore `delete` spesso ha un comportamento inaspettato e può solo essere +usato con sicurezza per eliminare proprietà esplicitamente impostate in oggetti +normali. diff --git a/external/JavaScript-Garden/doc/it/core/eval.md b/external/JavaScript-Garden/doc/it/core/eval.md new file mode 100644 index 0000000..54cf456 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/core/eval.md @@ -0,0 +1,49 @@ +## Perché non usare `eval` + +La funzione `eval` eseguirà una stringa di codice JavaScript nello scope locale. + + var number = 1; + function test() { + var number = 2; + eval('number = 3'); + return number; + } + test(); // 3 + number; // 1 + +Comunque, `eval` esegue solo nello scope locale quando viene chiamata +direttamente *e* quando il nome della funzione chiamata è `eval`. + + var number = 1; + function test() { + var number = 2; + var copyOfEval = eval; + copyOfEval('number = 3'); + return number; + } + test(); // 2 + number; // 3 + +L'uso di `eval` dovrebbe essere evitato. Il 99.9% dei suoi "utilizzi" può +essere ottenuto **senza** di essa. + +### `eval` sotto mentite spoglie + +Le [funzioni di timeout](#other.timeouts) `setTimeout` e `setInterval` possono +entrambe accettare una stringa come loro primo argomento. Questa stringa verrà +**sempre** eseguita nello scope globale dato che `eval` non viene chiamato +direttamente in questo caso. + +### Problemi di sicurezza + +`eval` è anche un problema di sicurezza, perché essa esegue **qualsiasi** +codice le viene passato. Non si dovrebbe **mai** usare con stringhe di origine +sconosciuta o inaffidabile. + +### In conclusione + +`eval` non dovrebbe mai essere usata. Qualsiasi codice che ne faccia uso dovrebbe +essere messo in discussione sotto l'aspetto della funzionalità, della performance +e della sicurezza. Se qualcosa richiede `eval` per poter funzionare, allora **non** +dovrebbe essere usato in primo luogo, ma si dovrebbe prevedere una +*miglior progettazione* che non richieda l'uso di `eval`. diff --git a/external/JavaScript-Garden/doc/it/core/semicolon.md b/external/JavaScript-Garden/doc/it/core/semicolon.md new file mode 100644 index 0000000..22ed609 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/core/semicolon.md @@ -0,0 +1,120 @@ +## Inserimento automatico dei punti-e-virgola + +Sebbene JavaScript utilizzi lo stile di sintassi del C, esso **non** +obbliga l'uso dei punti-e-virgola nel codice sorgente, perciò è possibile +ometterli. + +Detto questo, JavaScript non è un linguaggio che fa a meno dei punti-e-virgola. +Infatti, esso necessita di punti-e-virgola per poter comprendere il codice +sorgente. Quindi, il parser del JavaScript li inserisce **automaticamente** +ogni volta che incontra un errore di analisi dato dalla mancanza di un +punto-e-virgola. + + var foo = function() { + } // errore di analisi, atteso punto-e-virgola + test() + +Quindi avviene l'inserimento, ed il parser prova nuovamente. + + var foo = function() { + }; // nessun errore, il parser continua + test() + +L'inserimento automatico dei punti-e-virgola è considerato essere uno dei +**più grandi** errori di progettazione del linguaggio, perché *può* +modificare il comportamento del codice. + +### Come funziona + +Il codice qui sotto non ha punti-e-virgola, quindi sta al parser decidere dove +inserirli. + + (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) + +Di seguito il risultato del gioco da "indovino" del parser. + + (function(window, undefined) { + function test(options) { + + // Non inserito, linee unite + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- inserito + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- inserito + + return; // <- inserito, invalida l'istruzione return + { // trattato come un blocco + + // un'etichetta e una singola espressione + foo: function() {} + }; // <- inserito + } + window.test = test; // <- inserito + + // Le linee vengono unite nuovamente + })(window)(function(window) { + window.someLibrary = {}; // <- inserito + + })(window); //<- inserito + +> **Nota:** il parser del JavaScript non gestisce "correttamente" le istruzioni +> return che sono seguite da un ritorno a capo. Mentre questo non è necessariamente +> da considerarsi un errore dell'inserimento automatico di punti-e-virgola, esso +> può ancora essere considerato un effetto collaterale indesiderato. + +Il parser ha drasticamente modificato il comportamento del codice. In alcuni casi, +questo porta ad eseguire **cose sbagliate**. + +### Parentesi ad inizio riga + +Nel caso di parentesi ad inizio riga, il parser **non** inserirà un punto-e-virgola. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Questo codice viene trasformato in una sola linea. + + log('testing!')(options.list || []).forEach(function(i) {}) + +Le possibilità che `log` **non** ritorni una funzione sono **veramente** alte, +perciò il codice qui sopra porterà ad un `TypeError` dichiarando che +`undefined is not a function` (undefined non è una funzione). + +### In conclusione + +È fortemente raccomandato di non omettere **mai** i punti-e-virgola. +Si raccomanda anche di mantenere le parentesi sulla stessa linea della +corrispondente istruzione, e di non ometterle mai in istruzioni `if` / `else` +a linea singola. Queste misure precauzionali non solo miglioreranno la +consistenza del codice, ma preverranno anche che il parser JavaScript +modifichi il comportamento del codice in modo inaspettato. + diff --git a/external/JavaScript-Garden/doc/it/core/undefined.md b/external/JavaScript-Garden/doc/it/core/undefined.md new file mode 100644 index 0000000..c94d5b6 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/core/undefined.md @@ -0,0 +1,74 @@ +## `undefined` e `null` + +JavaScript usa due valori distinti per il nulla, `null` e `undefined`, e +quest'ultimo è il più utile. + +### Il valore `undefined` + +`undefined` è un tipo con esattamente un valore: `undefined`. + +Il linguaggio definisce anche una variabile globale che ha il valore di `undefined`. +Questa variabile è anche chiamata `undefined`. Comunque, questa variabile **non** è +né una costante né una parola chiave del linguaggio. Ciò significa che il suo *valore* +può facilmente essere sovrascritto. + +> **ES5 Nota:** `undefined` in ECMAScript 5 **non è più** *scrivibile* in strict +> mode, ma il suo nome può ancora essere sostituito da, per esempio, una funzione +> con nome `undefined`. + +Ecco alcuni esempi di quando il valore `undefined` viene ritornato: + + - Accedendo la variabile globale (non modificata) `undefined`. + - Accedendo una variabile dichiarata *ma non* ancora inizializzata. + - Ritorno implicito da funzioni che non hanno l'istruzione `return`. + - Istruzioni `return` che non ritornano esplicitamente alcun valore. + - Ricerca di proprietà inesistenti. + - Parametri funzione a cui non viene esplicitamente passato alcun valore. + - Qualsiasi cosa a cui sia stato assegnato il valore `undefined`. + - Qualsiasi espressione nella forma di `void(espressione)`. + +### Gestire le modifiche al valore di `undefined` + +Dato che la variabile globale `undefined` mantiene solo una copia dell'attuale +valore di `undefined`, assegnandole un nuovo valore **non** cambia il valore del +*tipo* `undefined`. + +Inoltre, per confrontare qualcosa con il valore di `undefined`, è necessario +ottenere prima il valore di `undefined`. + +Per proteggere il codice da possibili sovrascritture della variabile `undefined`, +viene usata una comune tecnica che prevede l'aggiunta di un ulteriore parametro +ad un [contenitore anonimo](#function.scopes) al quale non viene passato alcun +argomento. + + var undefined = 123; + (function(something, foo, undefined) { + // ora undefined nello scope locale + // fa nuovamente riferimento al valore `undefined` + + })('Hello World', 42); + +Un altro modo per ottenere lo stesso effetto sarebbe quello di usare una +dichiarazione all'interno del contenitore. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +La sola differenza è che questa versione si traduce in 4 byte in più quando +minificata, e non c'è nessun'altra istruzione `var` al'interno del contenitore +anonimo. + +### Utilizzi di `null` + +Mentre `undefined` nel contesto del linguaggio JavaScript viene principalmente +usato come un tradizionale *null*, l'attuale `null` (sia letterale che tipo di +dati) è più o meno solo un altro tipo di dato. + +Viene usato in alcune funzioni interne al JavaScript (come la dichiarazione +del termine della catena di prototipi, impostando `Foo.prototype = null`), ma +nella maggior parte dei casi, può essere rimpiazzato da `undefined`. + diff --git a/external/JavaScript-Garden/doc/it/function/arguments.md b/external/JavaScript-Garden/doc/it/function/arguments.md new file mode 100644 index 0000000..518c9b6 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/arguments.md @@ -0,0 +1,123 @@ +## L'oggetto `arguments` + +Ogni scope di funzione in JavaScript può accedere alla speciale variabile +`arguments`. Questa variabile mantiene un elenco di tutti gli argomenti +che sono stati passati alla funzione. + +> **Nota:** nel caso `arguments` sia stato già definito nello scope della +> funzione tramite una dichiarazione `var` o come parametro formale, +> l'oggetto `arguments` non sarà creato. + +L'oggetto `arguments` **non** è un `Array`. Sebbene abbia in parte la +semantica di un array (nello specifico la proprietà `length`), esso non +eredita da `Array.prototype` ed è a tutti gli effetti un `Object`. + +Proprio per questo motivo, **non** è possibile usare su `arguments` i metodi +standard degli array come `push`, `pop`, `slice`. E mentre l'iterazione con +un semplice ciclo `for` funzionerà senza problemi, sarà necessario convertire +l'oggetto in un vero `Array` per poter usare i metodi standard di `Array` con +esso. + +### Conversione ad array + +Il codice seguente ritornerà un nuovo `Array` contenenente tutti gli elementi +dell'oggetto `arguments`. + + Array.prototype.slice.call(arguments); + +Dato che questa conversione è **lenta**, **non è raccomandato** usarla in sezioni +di codice in cui la performance è un fattore critico. + +### Passaggio di argomenti + +Quello che segue è il metodo raccomandato per passare argomenti da una funzione +ad un'altra. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // codice da eseguire + } + +Un altro trucco è quello di usare `call` e `apply` insieme per creare veloci +contenitori senza vincoli. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Crea una versione senza vincoli di "method" + // Richiede i parametri: this, arg1, arg2...argN + Foo.method = function() { + + // Risultato: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Parametri formali e indici degli argomenti + +L'oggetto `arguments` crea funzioni *getter* e *setter* sia per le sue +proprietà che per i parametri formali della funzione. + +Come risultato, la modifica del valore di un parametro formale modificherà +anche il valore della corrispondente proprietà nell'oggetto `arguments`, e +vice versa. + + 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); + +### Miti e verità sulla performance + +Il solo caso in cui l'oggetto `arguments` non viene creato, è quando esso +viene dichiarato come un nome all'interno di una funzione o uno dei suoi +parametri formali. Non importa che venga usato o meno. + +Sia i *getter* che i *setter* vengono **sempre** creati. Perciò, il loro +utilizzo non ha praticamente alcun impatto sulle prestazioni, specialmente +nel mondo reale dove nel codice c'è più di un semplice accesso alle proprietà +dell'oggetto `arguments`. + +> **ES5 Nota:** questi *getter* e *setter* non vengono creati in strict mode. + +Ad ogni modo, c'è un caso che ridurrà drasticamente la performance nei motori +JavaScript moderni. È il caso dell'utilizzo di `arguments.callee`. + + function foo() { + arguments.callee; // fa qualcosa con questo oggetto funzione + arguments.callee.caller; // e l'oggetto funzione chiamante + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // normalmente sarebbe sostituito con il suo codice... + } + } + +Nel codice qui sopra, `foo` non può più essere soggetto ad [inlining][1] +dal momento che necessita di conoscere sia se stesso che il suo chiamante. +Questo non solo annulla possibili guadagni prestazionali ottenibili con +l'inlining, ma spezza anche il principio di incapsulazione perché la funzione +ora potrebbe essere dipendente da uno specifico contesto di esecuzione. + +L'utilizzo di `arguments.callee` o di qualsiasi altra delle sue proprietà +è **altamente sconsigliato**. + +> **ES5 Nota:** In strict mode, `arguments.callee` lancierà un `TypeError` +> dato che il suo utilizzo è stato deprecato. + +[1]: http://en.wikipedia.org/wiki/Inlining + diff --git a/external/JavaScript-Garden/doc/it/function/closures.md b/external/JavaScript-Garden/doc/it/function/closures.md new file mode 100644 index 0000000..58bcaf4 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/closures.md @@ -0,0 +1,106 @@ +## Closures e riferimenti + +Una delle caratteristiche più potenti di JavaScript è la disponibilità delle +*closure*. Con le closure, gli scope hanno **sempre** accesso allo scope +più esterno nel quale sono state definite. Dal momento che il solo scope che +JavaScript ha è lo [scope di funzione](#function.scopes), tutte le funzioni, +per default, agiscono da closure. + +### Emulare variabili private + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Qui, `Counter` ritorna **due** closure: la funzione `increment` e `get`. +Entrambe mantengono un **riferimento** allo scope di `Counter` e, quindi, +hanno sempre accesso alla variabile `count` definita in quello scope. + +### Perché le variabili private funzionano + +Dato che non è possibile fare riferimento o assegnare scope in JavaScript, +**non** c'è modo per accedere alla variabile `count` dall'esterno. Il solo +modo per interagire con essa è tramite le due closure. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +Il codice sopra **non** modificherà la variabile `count` nello scope di `Counter`, +dato che `foo.hack` non è stato definito in **quello** scope. Invece, creerà +(o meglio, sostituirà) la variabile *globale* `count`. + +### Closure nei cicli + +Un errore che spesso viene fatto è quello di usare le closure all'interno dei +cicli, come se stessero copiando il valore della variabile dell'indice del ciclo. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Questo esempio **non** stamperà i numeri da `0` a `9`, ma semplicemente il +numero `10` dieci volte. + +La funzione *anonima* mantiene un riferimento ad `i`, ma al momento in cui +`console.log` viene richiamata, il `ciclo for` è già terminato, ed il valore +di `i` è stato impostato a `10`. + +Per ottenere l'effetto desiderato, è necessario creare una **copia** del valore +di `i`. + +### Evitare il problema del riferimento + +Per copiare il valore della variabile indice del ciclo, è meglio usare un +[contenitore anonimo](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +La funzione anonima più esterna viene chiamata immediatamente con `i` come +suo primo argomento e riceverà una copia del **valore** di `i` come suo +parametro `e`. + +La funzione anonima che viene passata a `setTimeout` ora ha un riferimento a +`e`, il cui valore **non** viene modificato dal ciclo. + +C'è anche un altro possibile modo per ottenere il medesimo risultato, e cioè +ritornare una funzione dal contenitore anonimo che avrà quindi lo stesso +comportamento del codice visto precedentemente. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +C'è un ulteriore modo per ottenere ciò, usando `.bind`, che può assegnare un +contesto `this` e degli argomenti ad una funzione. Esso funziona allo stesso +modo degli esempi precedenti + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } diff --git a/external/JavaScript-Garden/doc/it/function/constructors.md b/external/JavaScript-Garden/doc/it/function/constructors.md new file mode 100644 index 0000000..1273e87 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/constructors.md @@ -0,0 +1,129 @@ +## Costruttori + +I costruttori in JavaScript sono differenti da quelli di molti altri linguaggi. +Qualsiasi chiamata a funzione preceduta dalla parola chiave `new` agisce come +un costruttore. + +Dentro al costruttore (la funzione chiamata) il valore di `this` fa riferimento +al nuovo oggetto creato. Il [prototype](#object.prototype) di questo **nuovo** +oggetto viene impostato al `prototype` dell'oggetto funzione che è stato invocato +come costruttore. + +Se la funzione che è stata chiamata non ha un'istruzione `return` esplicita, +allora essa ritorna implicitamente il valore di `this` (il nuovo oggetto). + + function Person(name) { + this.name = name; + } + + Person.prototype.logName = function() { + console.log(this.name); + }; + + var sean = new Person(); + +Questo esempio chiama `Person` come costruttore ed imposta il `prototype` del +nuovo oggetto creato a `Person.prototype`. + +In caso di istruzione `return` esplicita, la funzione ritorna il valore +specificato da quell'istruzione, ma **solo** se il valore di ritorno è un +`Object`. + + function Car() { + return 'ford'; + } + new Car(); // un nuovo oggetto, non 'ford' + + function Person() { + this.someValue = 2; + + return { + name: 'Charles' + }; + } + new Person(); // l'oggetto ritornato ({name: 'Charles'}), escluso someValue + +Quando la parola chiave `new` viene omessa, la funzione **non** ritornerà un +nuovo oggetto. + + function Pirate() { + this.hasEyePatch = true; // imposta la proprietà nell'oggetto globale! + } + var somePirate = Pirate(); // somePirate è undefined + +Mentre l'esempio precedente potrebbe sembrare essere funzionante in alcuni +casi, a causa del modo in cui lavora [`this`](#function.this) in JavaScript, +esso userà l'*oggetto globale* come valore di `this`. + +### Factory (Fabbriche di oggetti) + +Per poter omettere la parola chiave `new`, la funzione costruttore deve +esplicitamente ritornare un valore. + + function Robot() { + var color = 'gray'; + return { + getColor: function() { + return color; + } + } + } + Robot.prototype = { + someFunction: function() {} + }; + + new Robot(); + Robot(); + +Entrambe le chiamate a `Robot` ritornano lo stesso risultato, un nuovo oggetto +creato con una proprietà chiamata `method`, che è una [Closure](#function.closures). + +Bisogna anche notare che la chiamata `new Robot()` **non** influisce sul prototipo +dell'oggetto ritornato. Mentre il prototipo sarà impostato con il nuovo oggetto +creato, `Robot` non ritornerà mai quel nuovo oggetto. + +Nell'esempio sopra, non c'è differenza funzionale nell'usare o meno la parola +chiave `new`. + +### Creare nuovi oggetti tramite factory + +Viene spesso raccomandato di **non** usare `new` perché una sua dimenticanza +può portare a bug potenzialmente insidiosi da risolvere. + +Per poter creare un nuovo oggetto, si dovrebbe invece usare una factory e +costruire un nuovo oggetto all'interno di quella factory. + + function CarFactory() { + var car = {}; + car.owner = 'nobody'; + + var milesPerGallon = 2; + + car.setOwner = function(newOwner) { + this.owner = newOwner; + } + + car.getMPG = function() { + return milesPerGallon; + } + return car; + } + +Sebbene questo esempio sia a prova di omissione della parola chiave `new` e +renda sicuramente più semplice l'utilizzo delle [variabili private](#function.closures), +esso ha alcuni aspetti negativi. + + 1. Usa più memoria dal momento che gli oggetti creati **non** condividono + i metodi di un prototipo. + 2. Per poter ereditare, la factory deve copiare tutti i metodi da un altro + oggetto oppure mettere quell'oggetto nel prototipo del nuovo oggetto. + 3. Perdere la catena di prototipi solo perché si vuole tralasciare la + parola chiave `new` è contrario allo spirito del linguaggio. + +### In conclusione + +Sebbene l'omissione della parola chiave `new` possa portare all'introduzione di +bug, **non** è certo un motivo per privarsi completamente dell'uso dei prototipi. +Alla fine si tratta di decidere quale sia la soluzione più adatta per +l'applicazione. È specialmente importante scegliere uno specifico stile +di creazione degli oggetti ed usarlo in maniera **consistente**. diff --git a/external/JavaScript-Garden/doc/it/function/general.md b/external/JavaScript-Garden/doc/it/function/general.md new file mode 100644 index 0000000..9181ad8 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/general.md @@ -0,0 +1,52 @@ +## Dichiarazioni ed espressioni di funzione + +Le funzioni in JavaScript sono oggetti di prima classe. Ciò significa che +possono essere usate come ogni altro valore. Un uso comune di questa +caratteristica è quello di passare una *funzione anonima* come funzione di +callback ad un'altra funzione, possibilmente asincrona. + +### La dichiarazione di `function` + + function foo() {} + +La funzione qui sopra viene [elevata](#function.scopes) (hoisted) prima +che inizi l'esecuzione del programma. Questo vuol dire che essa è disponibile +da un *qualsasi* punto dello scope in cui è stata *definita*, anche se +richiamata prima dell'effettiva definizione nel sorgente. + + foo(); // funziona perché foo è stata creata prima di eseguire il codice + function foo() {} + +### L'espressione `function` + + var foo = function() {}; + +Questo esempio assegna la funzione *anonima* alla variabile `foo`. + + foo; // 'undefined' + foo(); // questo solleva un TypeError + var foo = function() {}; + +Dato che `var` è una dichiarazione che eleva il nome di variabile `foo` +prima che l'esecuzione del codice inizi, `foo` è già dichiarata quando lo +script viene eseguito. + +Ma, dal momento che le assegnazioni avvengono solo a runtime, il valore di +`foo` sarà [undefined](#core.undefined) per default, prima che il relativo +codice sia eseguito. + +### Espressione di funzione con nome + +Un altro caso speciale è l'assegnazione di funzioni con nome. + + var foo = function bar() { + bar(); // funziona + } + bar(); // ReferenceError + +Qui, `bar` non è disponibile nello scope più esterno, dal momento che la +funzione viene assegnata solo a `foo`, mentre è disponibile all'interno di +`bar`. Ciò è dato dal modo in cui funziona la [risoluzione dei nomi](#function.scopes) +in JavaScript: il nome della funzione è *sempre* reso disponibile nello scope +locale della funzione stessa. + diff --git a/external/JavaScript-Garden/doc/it/function/scopes.md b/external/JavaScript-Garden/doc/it/function/scopes.md new file mode 100644 index 0000000..24f6d25 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/scopes.md @@ -0,0 +1,241 @@ +## Scope e spazi di nome (namespace) + +Sebbene JavaScript non abbia problemi con la sintassi delle parentesi +graffe per la definizione di blocchi, esso **non** supporta lo scope +per blocco, quindi, tutto ciò che il linguaggio ci mette a disposizione +è lo *scope di funzione*. + + function test() { // questo è uno scope + for(var i = 0; i < 10; i++) { // questo non è uno scope + // conta + } + console.log(i); // 10 + } + +> **Nota:** quando non usato in un'assegnazione, istruzione return o come +> argomento di una funzione, la notazione `{...}` verrà interpretata come +> una dichiarazione di blocco e **non** come un oggetto letterale. Questo, +> assieme all'[inserimento automatico dei punti-e-virgola](#core.semicolon), +> può portare ad errori alquanto subdoli. + +Anche gli spazi di nome (namespace) non sono gestiti in JavaScript, e ciò +significa che ogni cosa viene definita in un namespace *globalmente condiviso*. + +Ogni volta che ci si riferisce ad una variabile, JavaScript risale attraverso +tutti gli scope fino a che non la trova e, nel caso esso raggiunga lo scope +globale senza aver trovato il nome richiesto, solleva un `ReferenceError`. + +### Il problema delle variabili globali + + // script A + foo = '42'; + + // script B + var foo = '42' + +Questi due script **non** hanno lo stesso effetto. Lo script A definisce una +variabile chiamata `foo` nello scope *globale*, mentre lo script B definisce +una `foo` nello scope *attuale*. + +Ancora una volta. Questo esempio **non** sortisce lo *stesso effetto*: il +non utilizzo di `var` può avere importanti conseguenze. + + // scope globale + var foo = 42; + function test() { + // scope locale + foo = 21; + } + test(); + foo; // 21 + +L'omissione dell'istruzione `var` all'interno della funzione `test` sostituirà +il valore di `foo`. Sebbene questo possa non sembrare un grosso problema in +un primo momento, ritrovarsi con migliaia di linee di JavaScript senza +utilizzare `var` introdurrà orribili bug molto difficili da individuare. + + // scope globale + var items = [/* un elenco */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // scope di subLoop + for(i = 0; i < 10; i++) { // istruzione var omessa + // fai qualcosa di eccezionale! + } + } + +Il ciclo esterno terminerà dopo la prima chiamata a `subLoop`, dato che `subLoop` +sovrascriverà il valore globale di `i`. L'utilizzo di una `var` per il secondo ciclo +`for` avrebbe facilmente evitato questo errore. L'istruzione `var` non dovrebbe +**mai** essere omessa a meno che l'*effetto desiderato* non sia proprio quello +di influenzare lo scope esterno. + +### Variabili locali + +In JavaScript le sole sorgenti per le variabili locali sono i parametri +[funzione](#function.general) e le variabili dichiarate tramite l'istruzione +`var`. + + // scope globale + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // scope locale della funzione test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +Mentre `foo` e `i` sono variabili locali all'interno dello scope della funzione +`test`, l'assegnazione di `bar` sostituirà la variabile globale con lo stesso +nome. + +### Elevamento (hoisting) + +JavaScript **eleva** le dichiarazioni. Questo significa che le istruzioni `var` +e le dichiarazioni `function` verranno spostate in cima agli scope che le +racchiudono. + + 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]; + } + } + +Il codice qui sopra, viene trasformato prima che inizi l'esecuzione. JavaScript +sposta sia le istruzioni `var` che le dichiarazioni `function` in cima al più +vicino scope che le racchiude. + + // le istruzioni var vengono spostate qui + var bar, someValue; // di default a 'undefined' + + // la dichiarazione function viene spostata qui + function test(data) { + var goo, i, e; // il blocco scope mancante sposta qui queste istruzioni + if (false) { + goo = 1; + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // fallisce con un TypeError dato che bar è ancora 'undefined' + someValue = 42; // le assegnazioni non vengono influenzate dall'elevazione + bar = function() {}; + + test(); + +L'omissione del blocco di scope non solo muoverà le istruzioni `var` fuori dal +corpo dei cicli, ma renderà anche i risultati di certi costrutti `if` poco +intuitivi. + +Nel codice originale, sebbene l'istruzione `if` sembrasse modificare la +*variabile globale* `goo`, effettivamente essa va a modificare la *variabile locale* +(dopo che l'elevazione è stata eseguita). + +Senza la conoscenza dell'*elevazione*, uno potrebbe pensare che il codice +qui sotto sollevi un `ReferenceError`. + + // verifica se SomeImportantThing è stato inizializzato + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Ma ovviamente tutto funziona grazie al fatto che l'istruzione `var` è stata +spostata all'inzio dello *scope globale*. + + var SomeImportantThing; + + // qui altro codice potrebbe o meno inizializzare SomeImportantThing + + // ci assicuriamo che ci sia + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Ordine di risoluzione dei nomi + +Tutti gli scope in JavaScript, *scope globale* incluso, hanno lo speciale +nome [`this`](#function.this) definito in essi, che fa riferimento +all'*oggetto attuale*. + +Gli scope di funzione hanno anche il nome [`arguments`](#function.arguments) +definito in essi, che contiene gli argomenti passati alla funzione. + +Per esempio, cercando di accedere ad una variabile di nome `foo` all'interno +dello scope di una funzione, JavaScript effettuerà una ricerca del nome nel +seguente ordine: + + 1. Nel caso ci sia un'istruzione `var foo` nello scope attuale, usa quella. + 2. Se uno dei parametri funzione si chiama `foo`, usa quello. + 3. Se la funzione stessa si chiama `foo`, usa quella. + 4. Vai al successivo scope esterno e ricomincia dal numero **1**. + +> **Nota:** avere un parametro di nome `arguments` **preverrà** la creazione +> dell'oggetto `arguments` di default. + +### Spazi di nome (Namespace) + +Un comune problema associato al fatto di avere un solo spazio nomi globale, +è che facilmente si incappa in problemi dove i nomi di variabile si +sovrappongono. In JavaScript queso problema può essere facilmente evitato +con l'aiuto dei *contenitori anonimi*. + + (function() { + // "namespace" auto contenuto + + window.foo = function() { + // una closure esposta + }; + + })(); // esecue immediatamente la funzione + +Le funzioni anonime sono considerate [espressioni](#function.general), quindi +per poter essere richiamabili, esse devono prima essere valutate. + + ( // valuta la funzione dentro le parentesi + function() {} + ) // e ritorna l'oggetto funzione + () // richiama il risultato della valutazione + +Ci sono altri modi per valutare e chiamare direttamente l'espressione funzione +i quali, sebbene differenti nella sintassi, hanno tutti il medesimo effetto. + + // Alcuni modi per invocare direttamente la + !function(){}() + +function(){}() + (function(){}()); + // e così via... + +### In conclusione + +Si raccomanda sempre di usare un *contenitore anonimo* per incapsulare il +codice nel suo proprio namespace. Questo non solo protegge il codice da +eventuali conflitti con i nomi, ma permette anche una migliore modularizzazione +dei programmi. + +Inoltre, l'uso delle variabili globali è considerato una **cattiva pratica**. +**Qualsiasi** loro uso indica codice scritto male che è suscettibile ad errori +e difficile da mantenere. + diff --git a/external/JavaScript-Garden/doc/it/function/this.md b/external/JavaScript-Garden/doc/it/function/this.md new file mode 100644 index 0000000..258086a --- /dev/null +++ b/external/JavaScript-Garden/doc/it/function/this.md @@ -0,0 +1,124 @@ +## Come funziona `this` + +JavaScript ha una concezione differente di ciò a cui il nome speciale `this` +fa normalmente riferimento nella maggior parte degli altri linguaggi di +programmazione. Ci sono esattamente **cinque** differenti modi nei quali +il valore di `this` può essere associato nel linguaggio. + +### Lo scope globale + + this; + +Usando `this` nello scope globale, esso farà semplicemente riferimento +all'oggetto *globale*. + +### Richiamando una funzione + + foo(); + +Qui, `this` farà ancora riferimento all'oggetto *globale*. + +> **ES5 Nota:** in strict mode, il caso globale **non** esiste più. +> In quel caso `this` avrà invece il valore di `undefined`. + +### Richiamando un metodo + + test.foo(); + +In questo esempio, `this` farà riferimento a `test`. + +### Richiamando un costruttore + + new foo(); + +Una chiamata di funzione che viene preceduta dalla parola chiave `new` +agisce come un [costruttore](#function.constructors). Dentro la funzione, +`this` farà riferimento all'`Object` **appena creato**. + +### Impostazione esplicita di `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // l'array verrà espanso come mostrato sotto + foo.call(bar, 1, 2, 3); // risulterà in a = 1, b = 2, c = 3 + +Quando si usano i metodi `call` o `apply` di `Function.prototype`, il valore di +`this` all'interno della funzione chiamata viene **esplicitamente impostato** +al primo argomento della corrispondente chiamata di funzione. + +Come risultato, nell'esempio sopra, il *caso del metodo* **non** viene applicato, +e `this` all'interno di `foo` sarà impostato a `bar`. + +> **Nota:** `this` **non può** essere usato per far riferimento all'oggetto +> all'interno di un `Object` letterale. Perciò `var obj = {me: this}` **non** +> avrà come risultato `me` che fa riferimento ad `obj`, dato che `this` +> viene assegnato solo da uno dei cinque casi elencati. + +### Insidie comuni + +Mentre molti di questi casi hanno senso, il primo può essere considerato +un altro errore di progettazione del linguaggio perché non ha **mai** un +uso pratico. + + Foo.method = function() { + function test() { + // this viene impostato all'oggetto globale + } + test(); + }; + +Una comune credenza è che `this` all'interno di `test` faccia riferimento a +`Foo` mentre, invece, **non** è così. + +Per poter ottenere l'accesso a `Foo` dall'interno di `test`, si può creare +una variabile locale all'interno di `method` che faccia riferimento a `Foo`. + + Foo.method = function() { + var self = this; + function test() { + // Qui viene usato self invece di this + } + test(); + }; + +`self` è solo un normale nome di variabile, ma viene comunemente usato come +riferimento ad un `this` più esterno. Abbinato alle [closures](#function.closures) +può anche essere usato per passare il valore di `this`. + +Con l'introduzione di ECMAScript 5 è possibile usare il metodo `bind` combinato +con una funziona anonima + + Foo.method = function() { + var test = function() { + // this ora fa riferimento a Foo + }.bind(this); + test(); + }; + +### Metodi di asseganzione + +Un'altra cosa che **non** funziona in JavaScript è la creazione di un alias ad +una funzione, cioè l'**assegnazione** di un metodo ad una variabile. + + var test = someObject.methodTest; + test(); + +A causa della prima dichiarazione, `test` ora agisce da semplice chiamata a +funzione e quindi, `this` all'interno di essa non farà più riferimento a +`someObject`. + +Mentre l'assegnazione tardiva di `this` potrebbe sembrare una cattiva idea +in un primo momento, alla prova dei fatti è ciò che fa funzionare +l'[ereditarietà prototipale](#object.prototype). + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Quando `method` viene chiamato da un'istanza di `Bar`, `this` farà riferimento +a quell'istanza. diff --git a/external/JavaScript-Garden/doc/it/index.json b/external/JavaScript-Garden/doc/it/index.json new file mode 100644 index 0000000..b7b090e --- /dev/null +++ b/external/JavaScript-Garden/doc/it/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in italiano", + "description": "Guida alle peculiarità e i difetti del JavaScript.", + "sections": [ + { + "title": "Introduzione", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Oggetti", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Funzioni", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Array", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Tipi di dati", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Base", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Varie", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/it/intro/index.md b/external/JavaScript-Garden/doc/it/intro/index.md new file mode 100644 index 0000000..b9e7ceb --- /dev/null +++ b/external/JavaScript-Garden/doc/it/intro/index.md @@ -0,0 +1,51 @@ +## Introduzione + +**JavaScript Garden** è una collezione in continua crescita di documentazione +relativa alle parti più peculiari del linguaggio di programmazione JavaScript. +Il suo intento è quello di mostrare come evitare i più comuni errori, i +problemi legati alla performance e le cattive abitudini che i programmatori +JavaScript non esperti possono incontrare lungo il loro cammino di +approfondimento del linguaggio. + +L'obiettivo di JavaScript Garden **non** è quello di insegnarti JavaScript. +Una conoscenza pregressa del linguaggio è fortemenete consigliata, in modo da +capire gli argomenti trattati da questa guida. Per poter imparare le basi del +linguaggio, ti suggeriamo di leggere l'eccellente [guida][1] su Mozilla +Developer Network. + +## Gli autori + +Questa guida è il risultato del lavoro di due utenti di [Stack Overflow][2], +[Ivo Wetzel][3] (stesura) e [Zhang Yi Jiang][4] (progettazione). + +È attualmente mantenuto da [Tim Ruffles](http://truffles.me.uk). + +## Collaboratori + +- Veramente troppi da elencare qui, [vedi tutti i collaboratori](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + +## Hosting + +JavaScript Garden è ospitato su GitHub, ma [Cramer Development][7] ci supporta +con un mirror su [JavaScriptGarden.info][8]. + +## Licenza + +JavaScript Garden è pubblicato sotto la [licenza MIT][9] ed ospitato su +[GitHub][10]. Se trovi inesattezze o errori di battitura, ti prego di +[segnalare il problema][11] o fare un pull request sul nostro repository. +Puoi anche trovarci nella [stanza JavaScript][12] della chat di Stack +Overflow. + +[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/external/JavaScript-Garden/doc/it/object/forinloop.md b/external/JavaScript-Garden/doc/it/object/forinloop.md new file mode 100644 index 0000000..96fc2d2 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/object/forinloop.md @@ -0,0 +1,68 @@ +## Il ciclo `for in` + +Come per l'operatore `in`, il ciclo `for in` attraversa la catena di +prototipi quando itera tra le proprietà di un oggetto. + +> **Nota:** il ciclo `for in` **non** itererà alcuna proprietà che abbia +> il proprio attributo `enumerable` impostato su `false`. Ad esempio, +> la proprietà `lenght` di un array. + + // Modifichiamo Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // stampa sia bar che moo + } + +Dato che non è possibile modificare il comportamento del ciclo `for in`, +è necessario filtrare le proprietà indesiderate all'interno del ciclo stesso. +In ECMAScript 3 o precedente, questo può essere fatto usando il metodo +[`hasOwnProperty`](#object.hasownproperty) di `Object.prototype`. + +A partire da ECMAScript 5, `Object.defineProperty` può essere utilizzato con +`enumerbale` impostato a `false` per aggiungere proprietà agli oggetti (incluso +`Object`) senza che queste proprietà vengano enumerate. In questo caso è +ragionevole assumere che, nel codice di un'applicazione, ogni proprietà +enumerabile sia stata aggiunta per un motivo, ed quindi omettere `hasOwnProperty` +in quanto rende il codice più prolisso e meno leggibile. Nel codice delle +librerie `hasOwnProperty` dovrebbe essere ancora utilizzato, dato che non è +possibile presumere quali proprietà enumerabili siano presenti nella catena dei +prototipi. + + +> **Nota:** dato che `for in` attraversa sempre tutta la catena di prototipi, +> esso rallenterà per ogni strato aggiuntivo di ereditarietà aggiunto ad un +> oggetto. + +### Usare `hasOwnProperty` per il filtraggio + + // questo è il foo dell'esempio precedente + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Questa è la sola versione corretta da usare con le vecchie versioni di ECMAScript. +Proprio a causa dell'utilizzo di `hasOwnProperty`, **soltanto** `moo` verrà +stampato; mentre omettendone l'uso, il codice sarà soggetto ad errori nei casi +dove i prototipi nativi (ad esempio `Object.prototype`) sono stati estesi. + +Nelle nuove versioni di ECMAScript, le proprietà non enumerabili possono essere +definite con `Object.defineProperty`, riducendo il rischio di iterare sulle +proprietà non usando `hasOwnProperty`. È altresì importante stare attenti +quando si usano librerie come [Prototype][1], che ancora non sfruttano le nuove +funzionalità di ECMAScript. +Quando questo framework viene incluso, è sicuro che i cicli `for in` che non +utilizzano `hasOwnProperty` non funzioneranno. + +### In conclusione + +Si raccomanda di usare **sempre** `hasOwnProperty` in ECMAScript 3 o precedenti, +e nel codice delle librerie. Non si dovrebbe mai dare per scontato nell'ambiente +in cui il codice sta girando, se i prototipi nativi sono stati estesi o meno. A +partire da ECMAScript 5 `Object.defineProperty` rende possibile definire proprietà +non enumerabili ed omettere `hasOwnProperty` nel codice dell'applicazione. + +[1]: http://www.prototypejs.org/ diff --git a/external/JavaScript-Garden/doc/it/object/general.md b/external/JavaScript-Garden/doc/it/object/general.md new file mode 100644 index 0000000..bc47d3e --- /dev/null +++ b/external/JavaScript-Garden/doc/it/object/general.md @@ -0,0 +1,103 @@ +## Utilizzo di oggetti e proprietà + +Tutto in JavaScript funziona come un oggetto, con la sola eccezione di +[`null`](#core.undefined) e [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Un'idea comunemente errata è che i numeri letterali non possano essere +usati come oggetti. Questo a causa di una scorretta gestione da parte del +parser di JavaScript, che tenta di analizzare la *dot notation* di un +numero come se fosse un letterale in virgola mobile. + + 2.toString(); // solleva SyntaxError + +Esistono un paio di soluzioni che possono essere usate per far sì che i +numeri letterali vengano considerati come oggetti. + + 2..toString(); // il secondo punto viene correttamente riconosciuto + 2 .toString(); // notate lo spazio tra il numero e il punto + (2).toString(); // viene prima valutato 2 + +### Oggetti come un tipo di dato + +Gli oggetti in JavaScript possono anche essere usati come [*tabelle hash*][1] e +consistono principalmente di proprietà con un nome che mappano dei valori. + +Usando un oggetto letterale (notazione `{}`) è possibile creare un +semplice oggetto. Questo nuovo oggetto [eredita](#object.prototype) da +`Object.prototype` e non ha [proprietà](#object.hasownproperty) definite. + + var foo = {}; // un nuovo oggetto vuoto + + // un nuovo oggetto con una proprietà `test` con valore 12 + var bar = {test: 12}; + +### Accedere alle proprietà + +È possibile accedere alle proprietà di un oggetto in due modi. +Usando il punto oppure attraverso l'uso delle parentesi quadre. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // funziona + +Le due notazioni funzionano quasi in modo identico, con la sola differenza +che usando le parentesi quadre è possibile impostare dinamicamente le +proprietà ed il loro nome identificatore, cosa che altrimenti genererebbe +un errore di sintassi. + +### Cancellazione delle proprietà + +Il solo modo per rimuovere una proprietà da un oggetto è quello di usare +l'operatore `delete`. Impostando la proprietà a `undefined` o `null`, infatti, +si rimuove solo il *valore* associato alla proprietà, ma non la *chiave*. + + 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]); + } + } + +Il codice qui sopra, stamperà sia `bar undefined` che `foo null`. Soltanto +`baz` è stato rimosso, e quindi non compare nell'output. + +### Notazione delle chiavi + + var test = { + 'case': 'Parola chiave, scrivimi come stringa', + // solleva SyntaxError + delete: 'Parola chiave, anche io devo essere una stringa' + }; + +Le proprietà di un oggetto possono essere scritte sia come normali caratteri +che come stringhe. A causa di un altro errore di progettazione del parser di +JavaScript, il codice appena visto genererà un `SyntaxError` in ECMAScript +precedente alla versione 5. + +Questo errore nasce dal fatto che `delete` è una *parola chiave*, quindi, +deve essere scritta come una *stringa letterale* per assicurarsi che venga +correttamente interpretata dai vecchi motori JavaScript. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/it/object/hasownproperty.md b/external/JavaScript-Garden/doc/it/object/hasownproperty.md new file mode 100644 index 0000000..71226c1 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/object/hasownproperty.md @@ -0,0 +1,58 @@ +## `hasOwnProperty` + +Per verificare se un oggetto ha (possiede) una proprietà definita dentro +**se stesso** piuttosto che in qualche parte della sua +[catena di prototipi](#object.prototype), è necessario usare il metodo +`hasOwnProperty` che tutti gli oggetti ereditano da `Object.prototype`. + +> **Nota:** **non** è sufficiente verificare se una proprietà è `undefined`. +> La proprietà potrebbe benissimo esistere, ed il suo valore potrebbe essere +> impostato a `undefined`. + +`hasOwnProperty` è la sola cosa in JavaScript che si occupa delle proprietà +**senza** attraversare la catena di prototipi. + + // Modifichiamo 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 + +Solo `hasOwnProperty` darà il risultato atteso e corretto. Guarda la sezione +[cicli `for in`][#object.forinloop] per maggiori dettagli riguardo a quando +usare `hasOwnProperty` per iterare le proprietà di un oggetto. + +### `hasOwnProperty` come proprietà + +JavaScript non protegge il nome di proprietà `hasOwnProperty`. Quindi, se +esiste la possibilità che un oggetto possa avere una proprietà con questo +nome, è necessario usare un `hasOwnProperty` *esterno* per ottenere il +risultato corretto. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // ritorna sempre false + + // Usa un altro hasOwnProperty di Object e lo richiama con 'this' impostato a foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // E' anche possibile usare hasOwnProperty dal prototipo di + // Object per questo scopo + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + + +### In conclusione + +Usare `hasOwnProperty` è l'**unico** metodo affidabile per verificare +l'esistenza di una proprietà in un oggetto. È raccomandabile che +`hasOwnProperty` venga usata in molti casi in cui è necessario iterare le +proprietà di un oggetto, come descritto nella sezione [cicli `for in`](#object.forinloop). diff --git a/external/JavaScript-Garden/doc/it/object/prototype.md b/external/JavaScript-Garden/doc/it/object/prototype.md new file mode 100644 index 0000000..8f8111d --- /dev/null +++ b/external/JavaScript-Garden/doc/it/object/prototype.md @@ -0,0 +1,126 @@ +## Il prototipo + +JavaScript non segue il classico modello di ereditarietà ma, piuttosto, +utilizza quello *prototipale*. + +Anche se ciò viene considerato come uno dei punti più deboli del JavaScript, +il modello ad ereditarietà prototipale è difatto più potente di quello +classico. Ad esempio, è piuttosto semplice creare un modello classico +sulle basi di quello prototipale, mentre l'operazione inversa è piuttosto +complessa. + +JavaScript è il solo linguaggio ampiamente utilizzato che sfrutta l'ereditarietà +prototipale, quindi è possibile prendersi il proprio tempo per adeguarsi alle +differenze esistenti tra i due modelli. + +La prima grande differenza è che l'ereditarietà in JavaScript utilizza le +*catene di prototipi*. + +> **Nota:** il semplice utilizzo di `Bar.prototype = Foo.prototype` avrà come +> risultato che entrambe gli oggetti condivideranno lo **stesso** prototipo. +> Quindi le modifiche ad uno dei prototipi degli oggetti, si rifletteranno +> anche sull'altro prototipo, che nella maggior parte dei casi non è l'effetto +> che vogliamo. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Imposta il prototipo di Bar ad una nuova istanza di Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Si assicura di elencare Bar come l'attuale costruttore + Bar.prototype.constructor = Bar; + + var test = new Bar(); // crea una nuova istanza di bar + + // La catena di prototipi finale + test [istanza di Bar] + Bar.prototype [istanza di Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* ecc. */ } + +Nel codice qui sopra, l'oggetto `test` erediterà sia da `Bar.prototype` che da +`Foo.prototype`, e quindi avrà accesso alla funzione `method` che era stata +definita in `Foo`. Avrà anche accesso alla proprietà `value` dell'**unica** +istanza di `Foo`, cioè il suo prototipo. È importante notare come +`new Bar()` **non** crei una nuova istanza di `Foo`, ma piuttosto riutilizzi +quella assegnata al suo prototipo. Perciò, tutte le istanze di `Bar` +condivideranno la **stessa** proprietà `value`. + +> **Nota:** **non** usare `Bar.prototype = Foo`, dal momento che questo non +> lo farà puntare al prototipo di `Foo`, ma piuttosto alla funzione oggetto +> `Foo`. Quindi la catena di prototipi passerà per `Function.prototype` +> invece che `Foo.prototype`. Perciò, `method` non sarà presente nella catena +> di prototipi. + +### Tabella delle proprietà + +Quando si accede alle proprietà di un oggetto, JavaScript **risale** la +catena di prototipi fino a che non incontra una proprietà con il nome +richiesto. + +Se raggiunge la cima della catena (cioè `Object.prototype`) senza aver +trovato le specifica proprietà, ritorna il valore [undefined](#core.undefined). + +### La proprietà Prototype + +Anche se la proprietà prototype viene usata dal linguaggio per creare la +catena di prototipi, è comunque sempre possibile assegnarvi un **qualsiasi** +dato valore. Nonostante cio, i dati primitivi verranno semplicemente ignorati +quando assegnati ad un prototipo. + + function Foo() {} + Foo.prototype = 1; // nessun effetto + +L'assegnazione di oggetti, come mostrato nell'esempio precedente, funzionerà, +e permette la creazione dinamica di catene di prototipi. + +### Performance + +Il tempo di ricerca per proprietà presenti in alto (all'inizio) della catena +di prototipi, può avere un impatto negativo sulla performance, e questo deve +essere tenuto bene in considerazione in codice dove la performance è un fattore +critico. Inoltre, il tentativo di accedere a proprietà inesistenti obbligherà +comunque ad attraversare tutta la catena di prototipi. + +Oltre a ciò, [iterando](#object.forinloop) tra le proprietà di un oggetto, +**ogni** proprietà presente nella catena di prototipi verrà enumerata. + +### Estensione di prototipi nativi + +Una caratteristica che viene spesso abusata, è quella di estendere +`Object.prototype` o uno degli altri prototipi interni al linguaggio. + +Questa tecnica viene detta [monkey patching][1] e vìola il principio di +*incapsulamento*. Anche se usata da popolari framework come [Prototype][2], +non c'è una valida ragione per pasticciare, aggiungendo ai tipi interni del +linguaggio funzionalità **non standard**. + +La **sola** buona ragione per estendere un prototipo interno è quella di +effettuare il backport di funzionalità presenti nei motori JavaScript +più recenti, come ad esempio [`Array.forEach`][3]. + +### In conclusione + +È **essenziale** capire il modello di ereditarietà prototipale prima +di scrivere codice complesso che ne faccia uso. Bisogna, inoltre, tenere +sotto controllo la lunghezza della catena di prototipi nel proprio codice, +e suddividerla in più catene se necessario, per evitare possibili problemi di +performance. Inoltre, i prototipi nativi non dovrebbero **mai** essere +estesi a meno che non sia per garantire compatibilità con le funzionalità +più recenti di JavaScript. + +[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/external/JavaScript-Garden/doc/it/other/timeouts.md b/external/JavaScript-Garden/doc/it/other/timeouts.md new file mode 100644 index 0000000..da216d5 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/other/timeouts.md @@ -0,0 +1,180 @@ +### `setTimeout` e `setInterval` + +Dato che JavaScript è asincrono, è possibile programmare l'esecuzione di una +funzione usando le funzioni `setTimeout` e `setInterval`. + +> **Nota:** i timeout **non** sono parte dello standard ECMAScript. Erano stati +> implementati in [BOM, o DOM Level 0][1], i quali non sono mai stati definiti +> o documentati formalmente. Nessuna specifica raccomandazione è mai stata +> pubblicata, tuttavia, la loro standardizzazione è in via di ultimazione con +> [HTML5][2]. Per loro stessa natura, quindi, l'implementazione può essere +> differente nei vari browser e motori di rendering. + + function foo() {} + var id = setTimeout(foo, 1000); // ritorna un Number > 0 + +Quando chiamato, `setTimeout` ritorna l'ID del timeout e programma `foo` per +essere eseguito **approssimativamente** un migliaio di millisecondi nel futuro. +`foo` verrà quindi eseguito **una volta**. + +Dipendendo dalla risoluzione del timer del motore JavaScript che esegue il codice, +come anche dal fatto che JavaScript è single threaded e quindi altro codice +potrebbe essere eseguito bloccando il thread, **non è mai** sicuro scommettere +che una funzione verrà eseguita esattamente al ritardo specifiato nella chiamata +a `setTimeout`. + +La funzione che è stata passata come primo parametro verrà chiamata dall'*oggetto globale*, +e ciò significa che [`this`](#function.this) all'interno della funzione chiamata +farà riferimento all'oggetto globale. + + function Foo() { + this.value = 42; + this.method = function() { + // this fa riferimento all'oggetto globale + console.log(this.value); // stamperà undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **Nota:** dato che `setTimeout` accetta un **oggetto funzione** come suo +> primo parametro, un errore comune è quello di usare `setTimeout(foo(), 1000)`, +> che userà il **valore di ritorno** della chiamata a `foo` e **non** `foo`. +> Questo è, la maggior parte delle volte, un errore silenzioso, dato che quando +> la funzione ritorna `undefined` `setTimeout` **non** solleverà alcun errore. + +### Sovrapposizione di chiamate con `setInterval` + +Mentre `setTimeout` esegue solo una volta la funzione, `setInterval` (come il +nome suggerisce) eseguirà la funzione **ogni** `X` millisecondi, ma il suo +utilizzo è sconsigliato. + +Quando il codice che viene eseguito blocca la chiamata timeout, `setInterval` +eseguirà ancora più chiamate alla specifica funzione. Questo può, specialmente +con intervalli molto brevi, tradursi in chiamate a funzione che si sovrappongono. + + function foo(){ + // qualcosa che blocca per 1 secondo + } + setInterval(foo, 1000); + +Nel codice precedente, `foo` verrà chiamato una volta e quindi bloccherà per +un secondo. + +Mentre `foo` blocca il codice, `setInterval` continuerà a programmare ulteriori +chiamate ad essa. Ora, quando `foo` ha finito, ci saranno già **dieci** ulteriori +chiamate ad essa in attesa per essere eseguite. + +### Gestione di potenziale codice bloccante + +La soluzione più semplice, come anche la più controllabile, è quella di usare +`setTimeout` all'interno di se stessa. + + function foo(){ + // qualcosa che blocca per 1 secondo + setTimeout(foo, 1000); + } + foo(); + +Non solo questo incapsula la chiamata a `setTimeout`, ma previene anche la +sovrapposizione delle chiamate e da un controllo addizionale. `foo` stessa +può ora decidere se vuole continuare ad essere eseguita oppure no. + +### Pulizia manuale dei timeout + +La pulizia di timeout ed intervalli funziona passando il rispettivo ID a +`clearTimeout` o `clearInterval`, in base a quale `set` di funzioni è stato +usato precedentemente. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Pulizia di tutti i timeout + +Dato che non c'è un metodo interno per la pulizia di tutti i timeout e/o +intervalli, è necessario usare la forza bruta per poter raggiungere questo +scopo. + + // pulisce "tutti" i timeout + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Ma ci potrebbero ancora essere timeout che non vengono toccati da questo +numero arbitrario. Un altro modo per ottenere ciò, è considerare che l'ID +dato ad un timeout viene incrementato di uno ogni volta che si chiama +`setTimeout`. + + // pulisce "tutti" i timeout + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Sebbene questo funzioni con la maggior parte dei browser odierni, non è +specificato che gli ID debbano essere ordinati in quel modo e ciò potrebbe +anche cambiare in futuro. Perciò, si raccomanda di tener traccia di tutti +gli ID dei timeout, così che possano essere puliti in modo specifico. + +### Uso nascosto di `eval` + +`setTimeout` e `setInterval` possono anche accettare una stringa come loro +primo parametro. Questa caratteristica non dovrebbe essere **mai** usata +perché internamente fa uso di `eval`. + +> **Nota:** L'esatto funzionamento quando viene passata una stringa +> potrebbe differire nelle varie implementazioni di JavaScript. Per esempio, +> JScript di Microsoft usa il costruttore `Function` al posto di `eval`. + + function foo() { + // verrà chiamata + } + + function bar() { + function foo() { + // non verrà mai chiamata + } + setTimeout('foo()', 1000); + } + bar(); + +Dal momento che `eval` non viene chiamata [direttamente](#core.eval) in questo +caso, la stringa passata a `setTimeout` verrà eseguita nello *scope globale*. +Quindi, non verrà usata la variabile locale `foo` dallo scope di `bar`. + +Si raccomanda inoltre di **non** usare una stringa per passare argomenti alla +funzione che verrà chiamata da una delle funzioni di timeout. + + function foo(a, b, c) {} + + // non usare MAI questo + setTimeout('foo(1, 2, 3)', 1000) + + // Usare invece una funzione anonima + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Nota:** mentre è ancora possibile usare la sintassi +> `setTimeout(foo, 1000, 1, 2, 3)`, non la si raccomanda, dato che il suo +> utilizzo potrebbe portare ad errori subdoli quando usata con i +> [metodi](#function.this). +> Inoltre, la sintassi potrebbe non funzionare in alcune implementazioni di +> JavaScript. +> Ad esempio, Internet Explorer di Microsoft [**non** passa direttamente gli argomenti al callback](3). + +### In conclusione + +Una stringa non dovrebbe **mai** essere usata come parametro di `setTimeout` o +`setInterval`. È un chiaro segno di codice **veramente** pessimo, quando +gli argomenti necessitano di essere passati alla funzione che deve essere +chiamata. Dovrebbe invece essere passata una *funzione anonima* che si incarichi +di gestire l'effettiva chiamata. + +Inoltre, l'uso di `setInterval` dovrebbe essere evitato perché il suo schedulatore +non viene bloccato dall'esecuzione di JavaScript. + +[1]: http://www.nczonline.net/blog/2009/09/29/web-definitions-dom-ajax-and-more/ "Web definitions: DOM, Ajax, and more" +[2]: http://www.w3.org/TR/2014/WD-html5-20140617/webappapis.html#timers "6 Web application APIs - HTML5" +[3]: http://msdn.microsoft.com/en-us/library/ie/ms536753(v=vs.85).aspx "setTimeout method (Internet Explorer)" diff --git a/external/JavaScript-Garden/doc/it/types/casting.md b/external/JavaScript-Garden/doc/it/types/casting.md new file mode 100644 index 0000000..5da767f --- /dev/null +++ b/external/JavaScript-Garden/doc/it/types/casting.md @@ -0,0 +1,75 @@ +## Conversione di tipo (Type Casting) + +JavaScript è un linguaggio **debolmente tipizzato**, perciò esso applicherà +una *conversione di tipo* **ovunque** sia possibile. + + // Queste sono vere + new Number(10) == 10; // l'oggetto Number viene convertito + // in una primitiva numero tramite chiamata implicita + // al metodo Number.prototype.valueOf + + 10 == '10'; // String viene convertita in Number + 10 == '+10 '; // Stringa più assurda + 10 == '010'; // a ancora di più + isNaN(null) == false; // null viene convertito in 0 + // che ovviamente non è NaN + + // Queste sono false + 10 == 010; + 10 == '-10'; + +> **ES5 Nota:** i numeri letterali che iniziano per `0` vengono interpretati +> come ottali (base 8). Il supporto per gli ottali è stato **rimosso** nello +> strict mode di ECMAScript 5. + +Per evitare i problemi appena visti, l'uso +dell'[operatore di uguaglianza stretta](#types.equality) è **altamente** +raccomandato. Sebbene questo eviti molti dei comuni problemi, ci sono ancora +molti ulteriori problemi che possono essere generati dal sistema debolmente +tipizzato di JavaScript. + +### Costruttori di tipi interni + +I costruttori dei tipi interni del linguaggio, come `Number` e `String`, +funzionano in modo differente a seconda che venga usata o meno la +parola chiave `new`. + + new Number(10) === 10; // False, Object e Number + Number(10) === 10; // True, Number e Number + new Number(10) + 0 === 10; // True, a causa della conversione implicita + +L'uso di un tipo di dato interno come `Number` come costruttore, creerà un +nuovo oggetto `Number`, ma l'omissione della parola chiave `new` farà sì +che la funzione `Number` agisca da convertitore. + +Inoltre, il passaggio di valori letterali o non oggetto risulterà in un'ancora +maggiore conversione di tipo. + +La miglior opzione è quella di fare **esplicitamente** la conversione ad uno +dei tre possibili tipi. + +### Convertire in una stringa + + '' + 10 === '10'; // true + +Anteponendo una stringa vuota, un valore può facilmente essere convertito in +una stringa. + +### Convertire in un numero + + +'10' === 10; // true + +Usando l'operatore **unario** di addizione, è possibile convertire in un numero. + +### Convertire in un booleano + +Usando due volte l'operatore **not**, un valore può essere convertito in un +booleano. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true diff --git a/external/JavaScript-Garden/doc/it/types/equality.md b/external/JavaScript-Garden/doc/it/types/equality.md new file mode 100644 index 0000000..9a28919 --- /dev/null +++ b/external/JavaScript-Garden/doc/it/types/equality.md @@ -0,0 +1,75 @@ +## Uguaglianza e comparazioni + +JavaScript usa due differenti metodi per comparare l'uguaglianza dei +valori degli oggetti. + +### L'operatore di uguaglianza + +L'operatore di uguaglianza consiste di due segni di uguaglianza: `==`. + +JavaScript supporta la **tipizzazione debole**. Questo significa che +l'operatore di uguaglianza **converte** i tipi in modo da poterli +confrontare. + + "" == "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 + +Questa tabella mostra i risultati della conversione di tipo, ed è il +principale motivo per cui l'uso di `==` è ampiamente considerato una +cattiva pratica. Esso introduce bug difficili da rilevare a causa delle +complesse regole di conversione. + +Inoltre, c'è anche un impatto sulla performance quando entra in gioco la +conversione di tipo. Ad esempio, una stringa deve essere convertita in un +numero prima di poter essere confrontata con un altro numero. + +### L'operatore di uguaglianza stretta + +L'operatore di uguaglianza stretta consiste di **tre** segni di uguaglianza: `===`. + +Funziona come il normale operatore di uguaglianza, con l'eccezione di +**non** eseguire la conversione di tipo tra gli operandi. + + "" === "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 + +I risultati qui sono più chiari e permettono di identificare subito un problema +con il codice. Questo rende il codice più solido di un certo grado e fornisce anche +migliorie alla performance nel caso di operandi di tipo differente. + +### Comparazione di oggetti + +Nonostante `==` e `===` vengano definiti operatori di **uguaglianza**, essi +funzionano differentemente quando almeno uno degli operandi è un `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Qui, entrambe gli operatori confrontano per **identità** e **non** per +uguaglianza. Essi confrontano, cioè, che sia la stessa **istanza** dell'oggetto, +in modo molto simile a `is` in Python e la comparazione di puntatori in C. + +### In conclusione + +Si raccomanda calorosamente di usare solo l'operatore di **uguaglianza stretta**. +Nei casi dove è necessario che i tipi vengano convertiti, questa operazione +dovrebbe essere fatta [esplicitamente](#types.casting) piuttosto che essere +lasciata alle complesse regole di conversione del linguaggio. + diff --git a/external/JavaScript-Garden/doc/it/types/instanceof.md b/external/JavaScript-Garden/doc/it/types/instanceof.md new file mode 100644 index 0000000..8ff454f --- /dev/null +++ b/external/JavaScript-Garden/doc/it/types/instanceof.md @@ -0,0 +1,41 @@ +## L'operatore `instanceof` + +L'operatore `instanceof` confronta i costruttori dei suoi due operandi. +È utile soltanto per la comparazione di oggetti realizzati dal +programmatore. Se usato sui tipi interni del linguaggio, esso è +praticamente inutile alla stregua dell'[operatore typeof](#types.typeof). + +### Confronto di oggetti personalizzati + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Questo imposta Bar.prototype all'oggetto funzione Foo, + // ma non ad un'istanza di Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Uso di `instanceof` con i tipi nativi + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +Un'importante cosa da notare qui è che `instanceof` non funziona con oggetti +originati da differenti contesti JavaScript (ad esempio, differenti +documenti in un browser web), dato che i loro costruttori non saranno +esattamente lo stesso oggetto. + +### In conclusione + +L'operatore `instanceof` dovrebbe essere usato **solo** quando si ha a che fare +con oggetti personalizzati creati dal programmatore, che provengono dallo +stesso contesto JavaScript. Proprio come per l'operatore [`typeof`](#types.typeof), +ogni altro tipo di utilizzo dovrebbe essere **evitato**. + diff --git a/external/JavaScript-Garden/doc/it/types/typeof.md b/external/JavaScript-Garden/doc/it/types/typeof.md new file mode 100644 index 0000000..4c9732f --- /dev/null +++ b/external/JavaScript-Garden/doc/it/types/typeof.md @@ -0,0 +1,87 @@ +## L'operatore `typeof` + +L'operatore `typeof` (assieme a [`instanceof`](#types.instanceof)) è +probabilmente il più grande difetto di progettazione di JavaScript, +dato che è quasi **completamente inusabile**. + +Sebbene `instanceof` abbia ancora limitati casi d'uso, `typeof` ha realmente +un solo caso d'uso, che **non** è quello di verificare il tipo di un oggetto. + +> **Nota:** mentre `typeof` può anche essere richiamato usando una sintassi +> simile a quella di una funzione (ad esempio, `typeof(obj)`), questa non è +> una funzione. Le parentesi funzionano normalmente e il valore di ritorno +> viene usato come operando per l'operatore `typeof`. **Non** esiste una +> funzione `typeof`. + +### Tabella dei tipi di JavaScript + + Valore Classe Tipo + ------------------------------------- + "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 questa tabella, *Tipo* fa riferimento al valore ritornato dall'operatore `typeof`. +Come si può chiaramente vedere, questo valore è tutto fuorchè affidabile. + +*Classe* si riferisce al valore della proprietà interna `[[Class]]` di un oggetto. + +> **Da specifiche:** il valore di `[[Class]]` può essere una delle seguenti +> stringhe: `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, +> `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +Per ottenere il valore di `[[Class]]`, bisogna usare il metodo `toString` di +`Object.prototype`. + +### La classe di un oggetto + +Le specifiche forniscono esattamente un modo per accedere al valore di +`[[Class]]`, con l'uso di `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 + +Nel esempio qui sopra, `Object.prototype.toString` viene chiamato con il valore +di [this](#function.this) impostato all'oggetto di cui si vuole ottenere il +valore di `[[Class]]`. + +> **ES5 Nota:** per comodità, il valore di ritorno di `Object.prototype.toString` +> per `null` e `undefined` è stato **cambiato** da `Object` a `Null` e +> `Undefined` in ECMAScript 5. + +### Testare variabili non definite + + typeof foo !== 'undefined' + +Questo esempio verificherà se `foo` è stata attualmente dichiarata oppure no. +Un semplice referenziamento ad essa risulterebbe in un `ReferenceError`. +Questo è l'unico caso in cui `typeof` è utile a qualcosa. + +### In conclusione + +Per verificare il tipo di un oggetto, è altamente raccomandato l'utilizzo di +`Object.prototype.toString`, dato che questo è il solo modo affidabile per +fare ciò. Come mostrato nella tabella precedente, alcuni valori di ritorno +di `typeof` non sono definiti nelle specifiche, e ciò dimostra come essi +potrebbero differire tra implementazioni differenti. + +A meno che non si debba verificare se una variabile è definta, `typeof` +dovrebbe essere evitato. + diff --git a/external/JavaScript-Garden/doc/ja/array/constructor.md b/external/JavaScript-Garden/doc/ja/array/constructor.md new file mode 100644 index 0000000..17b6471 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/array/constructor.md @@ -0,0 +1,25 @@ +## `Array`コンストラクター + +`Array`コンストラクターはそのパラメーターの扱い方が曖昧なので、新しい配列を作る時には、常に配列リテラル - `[]`記法 - を使用する事を強くお勧めします。 + + [1, 2, 3]; // 結果: [1, 2, 3] + new Array(1, 2, 3); // 結果: [1, 2, 3] + + [3]; // Result: [3] + new Array(3); // 結果: [] + new Array('3') // 結果: ['3'] + +このケースの場合、`Array`コンストラクターに渡される引数は一つだけですが、その引数は`Number`になります。コンストラクターは、引数に値がセットされた`length`プロパティを伴った新しい*疎*配列を返します。特筆すべきなのは、新しい配列の`length`プロパティ**のみ**が、このようにセットされるという事です。実際の配列のインデックスは初期化されません。 + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, インデックスがセットされていない + +配列の長さが先行してセットされるという振舞いは、いくつかの場合に便利です。例えば、文字の繰り返しや、`for loop`を使用したコードの回避などの場合です。 + + new Array(count + 1).join(stringToRepeat); + +### 終わりに + +`Array`コンストラクターの使用は出来る限り避けてください。リテラルが当然望ましい形です。それらは、短かく明快な文法をもっている為に、コードの可読性を高めてくれます。 + diff --git a/external/JavaScript-Garden/doc/ja/array/general.md b/external/JavaScript-Garden/doc/ja/array/general.md new file mode 100644 index 0000000..7afc71d --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/array/general.md @@ -0,0 +1,41 @@ +## 配列の繰り返しとプロパティ + +JavaScriptの配列もまたオブジェクトですが、[`for in ループ`](#object.forinloop)を配列の繰り返し処理で使用することの良い理由は1つもありません。実際、配列に`for in`を使用**しない**為の正当な理由はたくさんあります。 + +> **注意:** JavaScriptの配列は*連想配列*では**ありません**。JavaScriptは[objects](#object.general)だけがキーバリューをマッピングするものです。 +> また、連想配列は順序を**保持**しますが、オブジェクトは**保持しません**。 + +`for in`ループはプロトタイプチェーン上の全てのプロパティを列挙するため、[`hasOwnProperty`](#object.hasownproperty)をそれらのプロパティの存在判定に使います。この為、通常の`for`ループよりも**20倍**遅くなります。 + +### 繰り返し + +配列の要素を繰り返すとのに、最高のパフォーマンスを出したければ昔ながらの`for`ループを使うのが一番です。 + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +上記の例では1つ追加の仕掛けがありますが、それは`l = list.length`によって配列の長さをキャッシュする部分です。 + +`length`プロパティは配列自身に定義されてはいますが、ループ中の繰り返しで毎回これを参照してしまうと、やはりオーバーヘッドが存在してしまいます。最近のJavaScriptエンジンはこのような場合に最適化する**はず**ですが、コードが新しいエンジンで実行されるかどうか、知る方法はありません。 + +実際には、キャッシュを抜きにするとループの結果はキャッシュされたものに比べてたった**半分の速度**にしかなりません。 + +### `length`プロパティ + +`length`プロパティの*ゲッター*は単に配列に含まれる要素の数を返すだけにも関わらず、*セッター*は配列を**トランケート**する為にも使用できます。 + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +より小さいlengthを割り当てると配列をトランケートしますが、lengthが大きくなっても配列には何も影響しません。 + +### 終わりに + +最高のパフォーマンスの為には、常に`for`ループを使用し、`length`プロパティをキャッシュする事をお勧めします。`for in`ループを配列で使用するのは、バグや最低のパフォーマンスの傾向があるコードを書く前兆になります。 + diff --git a/external/JavaScript-Garden/doc/ja/core/delete.md b/external/JavaScript-Garden/doc/ja/core/delete.md new file mode 100644 index 0000000..424cd33 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/core/delete.md @@ -0,0 +1,75 @@ +## `delete`演算子 + +端的に言って、JavaScriptの関数やその他の要素は`DontDelete`属性が設定されているので、グローバル変数を消去する事は*不可能*です。 + +### グローバルコードと関数コード + +変数や、関数がグローバルまたは[関数スコープ](#function.scopes)で定義された時は、そのプロパティは有効なオブジェクトかグローバルオブジェクトになります。このようなプロパティは属性のセットを持っていますが、それらの内の1つが`DontDelete`になります。変数や関数がグローバルや関数コードで宣言されると、常に`DontDelete`属性を作るために、消去できません。 + + // グローバル変数: + var a = 1; // DontDelete属性が設定される + delete a; // false + a; // 1 + + // 通常関数: + function f() {} // DontDelete属性が設定される + delete f; // false + typeof f; // "function" + + // 再代入も役に立たない: + f = 1; + delete f; // false + f; // 1 + +### 明示的なプロパティ + +明示的にプロパティを設定することが、通常通りの消去を可能にします。 + + // プロパティを明示的に設定する + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +上記の例の中で、`obj.x`と`obj.y`はそれぞれ`DontDelete`属性が無い為に消去できます。これが下記の例でも動作する理由です。 + + // IE以外では、これも動作する + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - ただのグローバルのvar + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +ここでは`a`. [`this`](#function.this)を消す為にグローバルオブジェクトと明示的に宣言した`a`をそのプロパティとして参照させて、消去する事を許可するトリックを使います。 + +IE(最低でも6-8で)は多少のバグがある為に、上記のコードは動作しません。 + + +### 関数の引数と組み込み引数 + +関数の通常の引数である、[`arguments` objects](#function.arguments)と組み込みのプロパティもまた、`DontDelete`が設定されています。 + + // 関数の引数とプロパティ: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### ホストオブジェクト + +`delete`演算子の挙動はホストオブジェクトにとって予測不可能になりかねません。仕様によりホストオブジェクトは、あらゆる挙動の実行が許可されている為です。 + +### 終わりに + +`delete`演算子は、しばしば予期せぬ挙動をします。唯一安全な使用方法は通常のオブジェクトに明示的に設定されたプロパティを扱う場合だけです。 diff --git a/external/JavaScript-Garden/doc/ja/core/eval.md b/external/JavaScript-Garden/doc/ja/core/eval.md new file mode 100644 index 0000000..846d32c --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/core/eval.md @@ -0,0 +1,39 @@ +## なぜ、`eval`を使ってはいけないのか + +`eval`関数はローカルスコープ中のJavaScriptコードの文字列を実行します。 + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +しかし、`eval`は**直接**ローカルスコープから呼ばれて、*かつ*呼んだ関数の名前が実際の`eval`でないと実行しません。 + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +`eval`の使用は**全てのコスト**を払ってでも回避するべきです。その「使用法」の99.9%で、これ**無し**でも実装できます。 + +### 偽装された`eval` + +[timeout functions](#other.timeouts)である`setTimeout`と`setInterval`はどちらも最初の引数として文字列を取る事ができます。この文字列は`eval`がこの場合直接呼ばれていないので、**常に**グローバルスコープで実行されてしまいます。 + +### セキュリティの問題 + +`eval`はまたセキュリティの問題もあります。なぜなら、**どんな**コードを与えられても実行してしまうからで、**絶対**に不明または信頼できない発行元の文字列は使ってはいけません。 + +### 終わりに + +`eval`は絶対に使用しないでください。これを使用しているどんなコードも、その働き、パフォーマンスやセキュリティについて問われてしまいます。`eval`が必要な場合でも、最初の段階で使用**しない**でください。*より良いデザイン*を使用するべきで、それには`eval`を使う必要性はありません。 + diff --git a/external/JavaScript-Garden/doc/ja/core/semicolon.md b/external/JavaScript-Garden/doc/ja/core/semicolon.md new file mode 100644 index 0000000..d3bf4a2 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/core/semicolon.md @@ -0,0 +1,102 @@ +## セミコロン自動挿入 + +JavaScriptはC言語スタイルのシンタックスを持っていますが、これはソースコードの中でセミコロンの使用を強制している事には**ならない**ので、これらを省略する事も可能です。 + +JavaScriptはセミコロン無しの言語ではありません。実際に、ソースコードを理解する為にもセミコロンは必要になります。ですので、JavaScriptのパーサーはセミコロンが無い事によるパースエラーを検出する度に、**自動的**にセミコロンを挿入します。 + + var foo = function() { + } // セミコロンが入っている事が期待されるので、パースエラーになる + test() + +挿入が起こると、パーサーはもう一度パースします。 + + var foo = function() { + }; // エラーが無いので、パーサーは次の解析をする + test() + +セミコロンの自動挿入は、コードの振る舞いを変えられる為に、言語の**最大**の欠陥の内の一つと考えられています。 + +### どのように動くか + +以下のコードはセミコロンが無いので、パーサーはどこにセミコロンを挿入するか決めなくてはなりません。 + + (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) + +下記がパーサーの「推測」ゲームの結果になります。 + + (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; // <- inserted, breaks the return statement + { // ブロックとして扱われる + + // a label and a single expression statement + foo: function() {} + }; // <- 挿入 + } + window.test = test; // <- 挿入 + + // 再度行がマージされる + })(window)(function(window) { + window.someLibrary = {}; // <- 挿入 + + })(window); //<- 挿入 + +> **注意点:** JavaScriptパーサーは、すぐ後に改行が続く return文を正しく扱いません。 +> これは必ずしも自動セミコロン挿入の欠点によるものではありませんが、 +> それもまた望まない副作用となりえます。 + + +パーサーは上記のコードの振舞いを劇的に変化させます。あるケースにおいては、**間違っている事**にもなってしまいます。 + +### 先頭の括弧 + +先頭に括弧がある場合、パーサーはセミコロンを挿入**しません**。 + + log('testing!') + (options.list || []).forEach(function(i) {}) + +このコードは1つの行に変形します。 + + log('testing!')(options.list || []).forEach(function(i) {}) + +`log`が関数を返さ**ない**確率は**とても**高いです。しかし、上記では`undefined is not a function`という`TypeError`が繰り返されます。 + +### 終わりに + +セミコロンを省略するのは**絶対**にお勧めしません。括弧を対応する文と同じ行に記述すること、および一行の`if / else`文に対して括弧を省略しないことが推奨されています。これら両方の処理がコードの整合性を高めてくれるだけでなく、JavaScriptパーサーの振舞いを変えてしまうのを防いでくれるでしょう。 diff --git a/external/JavaScript-Garden/doc/ja/core/undefined.md b/external/JavaScript-Garden/doc/ja/core/undefined.md new file mode 100644 index 0000000..ac8761a --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/core/undefined.md @@ -0,0 +1,53 @@ +## `undefined`と`null` + +JavaScriptは`nothing`を表す2つの別個の値を持っています。これら2つの内で`undefined`はより便利な存在です。 + +### `undefined`の値 + +`undefined`はただ1つの値`undefined`を持つ型です。 + +この言語はまた、`undefined`の値を持つグローバル変数を定義しています。この値もまた`undefined`と呼ばれています。しかし、この変数は **どちらも** 言語のキーワードではなく、定数です。この事はこの*値*は簡単に上書きされてしまうという事になります。 + +> **ES5での注意点:** ECMAScript 5での`undefined`は **もはや** strict modeでは *書き変えられない* +> ようになっています。しかし、この名前は`undefined`という名前の関数の例に痕跡が見られるだけです。 + +`undefined`が返される時の例をいくつか挙げます。 + + - (未定義の)グローバル変数`undefined`にアクセスした時 + - `return`文が無い為に、暗黙のうちに関数が返された時 + - 何も返されない`return`がある時 + - 存在しないプロパティを探索する時 + - 関数のパラメーターで明示的な値が何も無い時 + - `undefined`が設定された全ての値 + +### `undefined`の値に変更する処理 + +グローバル変数`undefined`のみが実際の`undefined`の*値*のコピーを保持するので、これに新しい値を代入しても`undefined`の*型* の値が変更される事は**ありません**。 + +まだ、`undefined`の値に対して何かしらの比較をしないといけない場合は、最初に`undefined`の値を取得する必要があります。 + +コードの`undefined`の変数の上書きを可能な限りしないよう保護する為には、一般的なテクニックとして[anonymous wrapper](#function.scopes)の引数にパラメーターを追加するというものがあります。 + + var undefined = 123; + (function(something, foo, undefined) { + // ローカルスコープではundefined。 + // ここで値に対して参照がされる + + })('Hello World', 42); + +同じ効果を得る事ができる別の方法として、ラッパーの内部での宣言を使うものがあります。 + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +これらの唯一の違いは、こちらのバージョンの方が4バイト余計に短縮できるという物です。また、他に`var`ステートメントは匿名ラッパーの中にはありません。 + +### `null`の使用 + +JavaScriptというプログラム言語のコンテキストの中では、`undefined`は主に伝統的な意味での*null*の意味で使用される事が多いです。実際の`null`(リテラルも型も両方)は多かれ少なかれ、単なるデータ型です。 + +それはJavaScriptの内部でいくつか使われています(プロトタイプチェーンの終わりに`Foo.prototype = null`という宣言をするようなもの)が、ほとんど全てのケースで、`undefined`に置き替える事が可能です。 diff --git a/external/JavaScript-Garden/doc/ja/function/arguments.md b/external/JavaScript-Garden/doc/ja/function/arguments.md new file mode 100644 index 0000000..cb0982c --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/arguments.md @@ -0,0 +1,98 @@ +## オブジェクトの`arguments` + +JavaScriptの全ての関数スコープは`arguments`と呼ばれる特別な変数にアクセスできます。この変数は関数が受け取った全ての引数を保持する変数です。 + +> **注意:** `arguments`が既に`var`や正式なパラメーターにより +> 関数のスコープ内部で定義されている場合は +> `arguments`オブジェクトは作られません。 + +`arguments`オブジェクトは`Array`では**ありません**。これは配列と同じような -`length`プロパティと名付けられています- 文法を持っていますが、`Array.prototype`を継承している訳では無いので、実際`Object`になります。 + +この為、`arguments`で`push`や`pop`、`slice`といった通常の配列メソッドは使用する事が**出来ません**。プレーンな`for`ループのような繰り返しでは上手く動作しますが、通常の`Array`メソッドを使いたい場合は本当の`Array`に変換しなければなりません。 + +### 配列への変換 + +下のコードは`arguments`オブジェクトの全ての要素を含んだ新しい`Array`を返します。 + + Array.prototype.slice.call(arguments); + +この変換は**遅い**です。コードのパフォーマンスに関わる重要な部分での使用は**推奨しません**。 + +### 引き数の受け渡し + +下記の例はある関数から別の関数に引数を引き渡す際に推奨される方法です。 + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +他のテクニックとして、高速で非結合のラッパーとして`call`と`apply`両方を一緒に使用するという物があります。 + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // "メソッド"の非結合バージョンを作成する + // このメソッドはthis, arg1, arg2...argNのパラメーターを持っている + Foo.method = function() { + + // 結果: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### 仮パラメーターと引数のインデックス + +`arguments`オブジェクトは*ゲッター*と*セッター*機能を自身のプロパティと同様に関数の仮パラメーターとして作成します。 + +結果として、仮パラメーターを変更すると`arguments`の対応する値も変更されますし、逆もしかりです。 + + 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); + +### パフォーマンスの神話と真実 + +`arguments`オブジェクトは、関数の内部の名前宣言と仮パラメーターという2つの例外を常に持ちながら生成されます。これは、使用されているかどうかは関係がありません。 + +*ゲッター*と*セッター*は両方とも**常に**生成されます。その為これを使用してもパフォーマンスに影響は全くといって言い程ありません。`arguments`オブジェクトのパラメーターに単純にアクセスしているような、実際のコードであれば尚更です。 + +> **ES5での注意:** strictモードでは、これら*ゲッター*と*セッター*は生成されません。 + +しかし、一つだけモダンJavaScriptエンジンにおいて劇的にパフォーマンスが低下するケースがあります。そのケースとは`arguments.callee`を使用した場合です。 + + function foo() { + arguments.callee; // この関数オブジェクトで何かする + arguments.callee.caller; // そして関数オブジェクトを呼び出す + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // 通常はインライン展開する + } + } + +上記のコードでは、`foo`は自身と自身の呼び出し元の両方を知らないと[インライン展開][1]の対象になる事が出来ません。この事は、インライン展開によるパフォーマンスの向上の機会を失くす事になり、また、特定のコンテクストの呼び出しに依存する関数のせいで、カプセル化が解除されてしまいます。 + +この為に`arguments.callee`を使用または、そのプロパティを**決して**使用しない事を**強く推奨**します。 + +> **ES5での注意:** strictモードでは、`arguments.callee`は推奨されていない為に +> `Typeerror`が返るようになっています。 + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/external/JavaScript-Garden/doc/ja/function/closures.md b/external/JavaScript-Garden/doc/ja/function/closures.md new file mode 100644 index 0000000..718f84c --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/closures.md @@ -0,0 +1,78 @@ +## クロージャと参照 + +JavaScriptの一番パワフルな特徴の一つとして*クロージャ*が使える事が挙げられます。これはスコープが自身の定義されている外側のスコープに**いつでも**アクセスできるという事です。JavaScriptの唯一のスコープは[関数スコープ](#function.scopes)ですが、全ての関数は標準でクロージャとして振る舞います。 + +### プライベート変数をエミュレートする + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +ここで`Counter`は**2つ**のクロージャを返します。関数`increment`と同じく関数`get`です。これら両方の関数は`Counter`のスコープを**参照**し続けます。その為、そのスコープ内に定義されている`count`変数に対していつもアクセスできるようになっています。 + +### なぜプライベート変数が動作するのか? + +JavaScriptでは、スコープ自体を参照・代入する事が出来無い為に、外部から変数`count`にアクセスする手段が**ありません**。唯一の手段は、2つのクロージャを介してアクセスする方法だけです。 + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +上記のコードは`Counter`のスコープ中にある変数`count`の値を変更する事は**ありません**。`foo.hack`は**その**スコープで定義されていないからです。これは(`Counter`内の変数`count`の変更)の代わりに*グローバル*変数`count`の作成 -または上書き- する事になります。 + +### ループ中のクロージャ + +一つ良くある間違いとして、ループのインデックス変数をコピーしようとしてか、ループの中でクロージャを使用してしまうというものがあります。 + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +上記の例では`0`から`9`の数値が出力される事は**ありません**。もっと簡単に`10`という数字が10回出力されるだけです。 + +**匿名**関数は`i`への**参照**を維持しており、同時に`forループ`は既に`i`の値に`10`をセットし終った`console.log`が呼ばれてしまいます。 + +期待した動作をする為には、`i`の値の**コピー**を作る必要があります。 + +### 参照問題を回避するには + +ループのインデックス変数をコピーする為には、[匿名ラッパー](#function.scopes)を使うのがベストです。 + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +外部の匿名関数は`i`を即座に第一引数として呼び出し、引数`e`を`i`の**値**のコピーとして受け取ります。 + +`e`を参照している`setTimeout`を受け取った匿名関数はループによって値が変わる事が**ありません。** + +他にこのような事を実現する方法があります。それは匿名ラッパーから関数を返してあげる事です。これは上記のコードと同じ振る舞いをします。 + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/external/JavaScript-Garden/doc/ja/function/constructors.md b/external/JavaScript-Garden/doc/ja/function/constructors.md new file mode 100644 index 0000000..94561c0 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/constructors.md @@ -0,0 +1,103 @@ +## コンストラクタ + +JavaScriptのコンストラクタは色々ある他のプログラム言語とは一味違います。`new`キーワードが付いているどんな関数呼び出しも、コンストラクタとして機能します。 + +コンストラクタ内部では -呼び出された関数の事です- `this`の値は新規に生成された`Object`を参照しています。この**新規**のオブジェクトの[`prototype`](#object.prototype)は、コンストラクタとして起動した関数オブジェクトの`prototype`に設定されています。 + +もし呼び出された関数が、`return`ステートメントを明示していない場合は、暗黙の了解で`this`の値を -新規のオブジェクトとして- 返します。 + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +上記は、`Foo`をコンストラクタとして呼び出し、新規に生成されたオブジェクトの`prototype`を`Foo.prototype`に設定しています。 + +明示的に`return`ステートメントがある場合、関数は返り値が`Object`である場合に**限り**ステートメントで明示した値を返します。 + + function Bar() { + return 2; + } + new Bar(); // 新しいオブジェクト + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // 返ってきたオブジェクト + +`new`キーワードが省略されている場合は、関数は新しいオブジェクトを返す事は**ありません**。 + + function Foo() { + this.bla = 1; // グローバルオブジェクトに設定される + } + Foo(); // undefinedが返る + +JavaScriptの[`this`](#function.this)の働きのせいで、上記の例ではいくつかのケースでは動作するように見える場合がありますが、それは*グローバルオブジェクト*が`this`の値として使用されるからです。 + +### ファクトリー + +`new`キーワードを省略するためには、コンストラクタ関数が明示的に値を返す必要があります。 + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +`Bar`で呼び出されたものは両方とも全く同じものものになります。これには、`method`と呼ばれるプロパティを持ったオブジェクトが新しく生成されますが、これは[クロージャ](#function.closures)です。 + +また、注意する点として呼び出された`new Bar()`は返ってきたオブジェクトのプロトタイプに影響**しません**。プロトタイプは新しく生成されたオブジェクトにセットされはしますが、`Bar`は絶対にその新しいオブジェクトを返さないのです。 + +上記の例では、`new`キーワードの使用の有無は機能的に違いがありません。 + + +### ファクトリーとして新しくオブジェクトを作成する + +多くの場合に推奨される事として、`new`の付け忘れによるバグを引き起こしやすいので、`new`を使用**しない**ようにするという事があります。 + +新しいオブジェクトを作成するためにファクトリーを使用して、そのファクトリー内部に新しいオブジェクトを作成すべきだという事です。 + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +上記の例では`new`キーワードが無いため堅牢になりますし、確実に[プライベート変数](#function.closures)を使用するのが簡単になりますが、いくつかの欠点があります。 + + 1. 作られたオブジェクトがプロトタイプ上のメソッドを共有しないために、よりメモリーを消費してしまいます。 + 2. ファクトリーを継承するために、他のオブジェクトの全てのメソッドをコピーする必要があるか、新しいオブジェクトのプロトタイプ上にそのオブジェクトを設置する必要があります。 + 3. `new`キーワードが無いという理由だけで、プロトタイプチェーンから外れてしまうのは、どことなく言語の精神に反します。 + +### 終わりに + +`new`キーワードが省略される事によりバグの可能性がもたらされますが、それによりプロトタイプを全く使わないという確かな理由には**なりません**。最終的には、アプリケーションの必要性により、どちらの解決法がより良いかが決まってきます。特に大切なのは、オブジェクトの作成に特定のスタイルを選ぶ事、また**そのスタイルに固執する事**です。 + + diff --git a/external/JavaScript-Garden/doc/ja/function/general.md b/external/JavaScript-Garden/doc/ja/function/general.md new file mode 100644 index 0000000..f53e2dd --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/general.md @@ -0,0 +1,37 @@ +## 関数の宣言と式 + +関数はJavaScriptの第一級オブジェクトです。この事は、その他の値と同じように渡す事が出来るという事です。この機能で良く使われる一つとして**匿名関数**を他のオブジェクトにコールバックとして渡すというものがあり、これで非同期での実装が可能になります。 + +### `関数`宣言 + + function foo() {} + +上記の関数はプログラムの開始時の前に評価されるように[巻き上げ](#function.scopes)られます。従って*定義*されたスコープ内の*どこでも*使用する事が可能になります。ソース内での実際の定義が呼ばれる前でもです。 + + foo(); // このコードが動作する前にfooが作られているので、ちゃんと動作する + function foo() {} + +### `関数`式 + + var foo = function() {}; + +この例では、`foo`という変数に無名で*匿名*の関数が割り当てられています。 + + foo; // 'undefined' + foo(); // これはTypeErrorが起こる + var foo = function() {}; + +`var`は宣言である為に、変数名`foo`がコードが開始される実際の評価時より前のタイミングにまで巻き上げられています。`foo`は既にスクリプトが評価される時には定義されているのです。 + +しかし、コードの実行時にのみこの割り当てがされるため、`foo`という変数は対応するコードが実行される前にデフォルト値である[undefined](#core.undefined)が代入されるのです。 + +### 名前付き関数式 + +他に特殊なケースとして、名前付き関数があります。 + + var foo = function bar() { + bar(); // 動作する + } + bar(); // ReferenceError + +この場合の`bar`は`foo`に対して関数を割り当てるだけなので、外部スコープでは使用できません。しかし、`bar`は内部では使用できます。これはJavaScriptの[名前解決](#function.scopes)の方法によるもので、関数名は*いつも*関数自身のローカルスコープ内で有効になっています。 diff --git a/external/JavaScript-Garden/doc/ja/function/scopes.md b/external/JavaScript-Garden/doc/ja/function/scopes.md new file mode 100644 index 0000000..ddbe08c --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/scopes.md @@ -0,0 +1,195 @@ +## スコープと名前空間 + +JavaScriptはブロックに2つのペアの中括弧を使うのが素晴しいですが、これはブロックスコープをサポートして**いません**。その為、この言語に残されているのは*関数スコープ*だけです。 + + function test() { // スコープ + for(var i = 0; i < 10; i++) { // スコープではない + // 数える + } + console.log(i); // 10 + } + +> **注意:** 代入が使用されてない時、return文や関数の引数、`{...}`表記はブロック文として +> 解釈されて、オブジェクトリテラルとは**なりません**。これは[セミコロン自動挿入](#core.semicolon) +> と連動して奇妙なエラーを引き起こすことになります。 + +JavaScriptはまた明確な名前空間を持ちません。この事は全て一つの*グローバルで共有された*名前空間で定義されるという事です。 + +変数が参照されるまでの間、JavaScriptはスコープ全てを遡って参照を探索します。グローバルスコープまで遡っても要求した名前が無いと`ReferenceError`が発生します。 + +### グローバル変数の致命傷 + + // スクリプト A + foo = '42'; + + // スクリプト B + var foo = '42' + +上記の2つのスクリプトは同じ効果を持って**いません**。スクリプト Aは`foo`と呼ばれる変数を、*グローバル*スコープに定義しており、スクリプト Bは`foo`を*現在*のスコープで定義ています。 + +繰り返しますが、この2つのスクリプトは*同じ影響*を全く持って**いない**スクリプトになります。`var`を使用しない事は重大な意味を持ちます。 + + // グローバルスコープ + var foo = 42; + function test() { + // ローカルスコープ + foo = 21; + } + test(); + foo; // 21 + +`test`関数の中の`var`ステートメントを省略すると`foo`の値をオーバーライドします。最初の内は大した事ではないように思いますが、JavaScriptが何千行規模になると、`var`を使っていない事でバグの追跡が酷く困難になります。 + + // グローバルスコープ + var items = [/* 何かのリスト */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // サブループのスコープ + for(i = 0; i < 10; i++) { // varステートメントが無くなった + // 素敵な実装を! + } + } + +外側のループは`subloop`が最初に呼ばれた後に終了します。なぜなら、`subloop`がグローバル変数`i`の値で上書きされているからです。2番目の`for`ループに`var`を使用する事によって簡単にこのエラーを回避する事ができます。*目的とする効果*を外側のスコープに与えようとしない限り、**絶対**に`var`ステートメントは省略してはいけません。 + +### ローカル変数 + +JavaScriptのローカル変数の為の唯一の作成方法は[function](#function.general)パラメーターと`var`ステートメントによって宣言された変数になります。 + + // グローバルスコープ + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // 関数testのローカル変数 + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo`と`i`は、関数`test`のスコープ内のローカル変数ですが、`bar`の代入は同じ名前でグローバル変数で上書きしてしまいます。 + +### 巻き上げ + +JavaScriptは宣言を**巻き上げ**ます。これは`var`ステートメントと`function`宣言が、それらを含むスコープの一番先頭に移動するという事を意味します。 + + 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]; + } + } + +上記のコードは、実行を開始する前に変換されてしまいます。JavaScriptは`var`ステートメントと同じように、直近で囲んでいる`function`宣言を先頭に移動させます。 + + // varステートメントはここに移動する + var bar, someValue; // 'undefined'がデフォルト + + // function宣言もここに移動する + function test(data) { + var goo, i, e; // 無くなったブロックスコープはこちらに移動する + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // barが'undefined'のままなので、Typeerrorで呼び出し失敗 + someValue = 42; // 割り当てすると巻き上げの影響を受けない + bar = function() {}; + + test(); + +ブロックスコープの欠落は`var`ステートメントをループやボディの外に移動するだけでなく、`if`の構成を直感的ではないものにしてしまいます。 + +元のコードの中の`if`ステートメントは*グローバル変数*である`goo`も変更しているように見えますが、実際には -巻き上げが適用された後に- *ローカル変数*を変更しています。 + +*巻き上げ*についての知識がないと、下に挙げたコードは`ReferenceError`になるように見えます。 + + // SomeImportantThingが初期化されているかチェックする + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +しかし、勿論上記の動きは`var`ステートメントが*グローバルスコープ*の上に移動しているという事実に基づいています。 + + var SomeImportantThing; + + // 他のコードがSomeImportantThingをここで初期化するかもしれないし、しないかもしれない + + // SomeImportantThingがある事を確認してください + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### 名前解決の順序 + +JavaScriptの*グローバルスコープ*を含む、全てのスコープは、*現在のオブジェクト*を参照している特殊な名前[`this`](#function.this)を持っています。 + +関数スコープはまた、[`arguments`](#function.arguments)という名前も持っています。それは関数スコープの中で定義され、関数に渡された引数を含んでいます。 + +例として、関数の中で`foo`と命名された変数にアクセスしようとする場合を考えましょう。JavaScriptは以下の順番で、その名前を探索しようとします。 + + 1. `var foo`ステートメントが現在のスコープで使われている場合 + 2. `foo`という名前の関数パラメーターが存在するかどうか + 3. 関数それ自体が`foo`として呼ばれているかどうか + 4. 一つ外のスコープに行き、再度**#1**から始める + +> **注意:** `arguments`と呼ばれるパラメーターを持つという事は、デフォルトの`arguments` +> オブジェクトを生成するのを**阻害**します。 + + +### 名前空間 + +一つしかグローバルの名前空間を持たない事による良くある問題は変数名の衝突による問題の起きる可能性です。JavaScriptでは、この問題を*匿名関数ラッパー*の助けで簡単に回避できます。 + + (function() { + // "名前空間"に自分を含む + + window.foo = function() { + // 露出したクロージャ + }; + + })(); // 即座に関数を実行する + + +無名関数は[expressions](#function.general)とみなされ、呼び出し可能になり最初に評価されます。 + + ( // カッコ内の関数が評価される + function() {} + ) // 関数オブジェクトが返される + () // 評価の結果が呼び出される + +関数式を評価し、呼び出す別の方法として構文は違いますが、同様の動作をするのが下記です。 + + // 2つの別の方法 + +function(){}(); + (function(){}()); + +### 終わりに + +自身の名前空間にカプセル化する為に常に*匿名関数ラッパー*を使用する事を推奨します。これは、コードを名前衝突から守る為だけでなく、プログラムのより良いモジュール化の為でもあります。 + +さらに、グローバル変数の使用は**悪い習慣**と考えられています。**一回**でもグローバル変数を使用するとエラーが発生しやすく、メンテナンスがしにくいコードになってしまいます。 + diff --git a/external/JavaScript-Garden/doc/ja/function/this.md b/external/JavaScript-Garden/doc/ja/function/this.md new file mode 100644 index 0000000..88a3539 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/function/this.md @@ -0,0 +1,93 @@ +## `this`はどのように動作するのか + +JavaScriptの`this`と名付けられた特殊なキーワードは他のプログラム言語と違うコンセプトを持っています。JavaScriptの`this`は正確に**5個**の別々の使い道が存在しています。 + +### グローバルスコープとして + + this; + +`this`をグローバルスコープ内で使用すると、単純に*グローバル*オブジェクトを参照するようになります。 + + +### 関数呼び出しとして + + foo(); + +この`this`は、再度*グローバル*オブジェクトを参照しています。 + +> **ES5での注意:** strictモードでは、このグローバルのケースは**もはや**存在していません。 +> この場合`this`は`undefined`値を代わりに持つことになります。 + +### メソッド呼び出しとして + + test.foo(); + +この例では`this`は`test`を参照します。 + +### コンストラクター呼び出し + + new foo(); + +`new`キーワードが付いた関数呼び出しは[コンストラクター](#function.constructors)として機能します。関数内部では`this`は*新規に作成された*`Object`を参照します。 + +### `this`の明示的な設定 + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // 配列は下記で展開される + foo.call(bar, 1, 2, 3); // 結果はa = 1, b = 2, c = 3 + + `Function.prototype`の`call`や`apply`メソッドを使用した時には、呼び出された関数の内部での`this`の値は、対応する関数呼び出しの最初の引数に**明示的に設定**されます。 + +結果として、上記の例では*メソッドケース*が適用**されず**、`foo`の内部の`this`は`bar`に設定されます。 + +> **注意:** `this`は`Object`リテラル内部のオブジェクトを参照**しません**。 +> ですので、`var obj = {me: this}`での`me`は`obj`を参照**しません**。 +> `this`はここで紹介ている5個のケースの内どれか一つに束縛されます。 + +### 良くある落し穴 + +これらのケースのほとんどは理にかなったものですが、最初のケースは実際に利用されることが**絶対**にないので、間違った言語設計だとみなせるでしょう。 + + Foo.method = function() { + function test() { + // このファンクションはグローバルオブジェクトに設定される + } + test(); + }; + +良くある誤解として`test`の中の`this`が`Foo`を参照しているというものがありますが、そのような事実は**一切**ありません。 + +`test`の中の`Foo`にアクセスする為には、`Foo`を参照する`method`のローカル変数を作る必要があります。 + + Foo.method = function() { + var that = this; + function test() { + // ここでthisの代わりに使用する + } + test(); + }; + +`that`は通常の変数名ですが、外部の`this`の参照の為に良く使われます。[クロージャ](#function.closures)と組み合わせる事で`this`の値を渡す事ができるようになります。 + +### メソッドの割り当て + +JavaScriptを使用する上で、もう一つ動か**ない**ものが関数のエイリアスです。これは変数へメソッドを**割り当て**する事です。 + + var test = someObject.methodTest; + test(); + +最初のケースの`test`は通常の関数呼び出しになる為に、この中の`this`は、もはや`someobject`を参照できなくなってしまいます。 + +`this`の遅延バインディングは最初見た時にはダメなアイデアに見えますが、[プロトタイプ継承](#object.prototype)により、きちんと動作します。 + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +`method`が`Bar`のインスタンスにより呼び出された時に、`this`はまさにそのインスタンスを参照するようになります。 diff --git a/external/JavaScript-Garden/doc/ja/index.json b/external/JavaScript-Garden/doc/ja/index.json new file mode 100644 index 0000000..00814ba --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden in Japanese", + "description": "JavaScriptの奇妙さと欠陥についてのガイドライン", + "sections": [ + { + "title": "前書き", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "オブジェクト", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "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" + ] + }, + { + "title": "コア", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "その他", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/ja/intro/index.md b/external/JavaScript-Garden/doc/ja/intro/index.md new file mode 100644 index 0000000..9ce45ec --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/intro/index.md @@ -0,0 +1,38 @@ +## 前書き + +**JavaScript Garden** はJavaScriptというプログラム言語の一番奇妙な部分についてのドキュメント集です。 +このドキュメントはJavaScriptという言語に慣れていないプログラマーがこの言語について深く知ろうとする際に遭遇する、良くある間違い・小さなバグ・パフォーマンスの問題・悪い習慣などを避ける為のアドバイスを与えます。 + +JavaScript GardenはJavaScriptを教える事を**目的にしていません**。このガイドの項目を理解する為には、この言語に対する前提知識がある事を推奨します。この言語の基礎部分についてはMozilla Developer Networkの[ガイド][1] がオススメです。 + +## 著者 + +このガイドは愛すべき[Stack Overflow][2]の2人のユーザー[Ivo Wetzel][3] +(執筆)と[Zhang Yi Jiang][4] (デザイン)によって作られました。 + +## 貢献者 + +- [貢献者](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## ホスティング + +JavaScript GardenはGitHubでホスティングされていますが、[Cramer Development][7]が[JavaScriptGarden.info][8]というミラーサイトを作ってくれています。 + +## ライセンス + +JavaScript Gardenは[MIT license][9]の下で公開されており、[GitHub][10]でホスティングされています。もしもエラーやtypoを見つけたら[file an issue][11]に登録するかリポジトリにプルリクエストを送ってください。 +またStack Overflowチャットの[JavaScript room][12]に私達はいます。 + + +[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/external/JavaScript-Garden/doc/ja/object/forinloop.md b/external/JavaScript-Garden/doc/ja/object/forinloop.md new file mode 100644 index 0000000..180620b --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/object/forinloop.md @@ -0,0 +1,42 @@ +## `for in`ループ + +`in`オペレーターは単に、`for in`ループの中でオブジェクトのプロパティをプロトタイプチェーンの中で繰り返し遡る為にあるものです。 + +> **注意:** `for in`ループは`enumerable`属性が`false`にセットされているプロパティを反復処理**しません**。; +> 例えば、配列の`length`プロパティなどがそれに当たります。 + + // Object.prototype汚染 + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // barとmooが両方とも表示される + } + +`for in`ループそれ自体の動作を変更する事は不可能ですが、ループ内にある要らないプロパティをフィルタリングする必要があります。そんな時は`Object.prototype`の[`hasOwnProperty`](#object.hasownproperty)メソッドを使うと解決します。 + +> **注意:** `for in`は常にプロトタイプチェーンを完全に遡ります。これにより +> オブジェクトに追加されている継承が多ければ多い程、速度は遅くなります。 + +### `hasOwnProperty`をフィルタリングに使用する + + // 継承されているfoo + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +このループの唯一正しい使い方がこの方法です。`hasOwnProperty`を使用しているので、 +`moo`**のみ**が表示されるようになります。`hasOwnProperty`が省略されている場合は、このコードは +組み込みのプロトタイプが存在する場合に(特に`Object.prototype`が拡張されている場合)エラーを発生しやすくなります。 + +一般に広く使用されているJavaScriptフレームワークとして[Prototype][1]が挙げられます。このフレームワークには、 +`for in` 内で`hasOwnProperty`が使用されプロトタプチェーン内を頭まで遡るのを中断する事が保証されています。 + +### 終わりに + +**常に**`hasOwnProperty`を使用する事を推奨します。コードの実行環境や、組み込みのプロトタイプが拡張されているかどうかを仮定して書くようなコードを絶対書いてはいけません。 + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/ja/object/general.md b/external/JavaScript-Garden/doc/ja/object/general.md new file mode 100644 index 0000000..1878247 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/object/general.md @@ -0,0 +1,83 @@ +## オブジェクトの使用法とプロパティ + +JavaScriptの全ての要素は2つの例外を除いて、オブジェクトのように振る舞います。 +その2つとは[`null`](#core.undefined)と[`undefined`](#core.undefined)です。 + + 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のオブジェクトは[*ハッシュマップ*][1]としても使用されます。これは名前付きのプロパティと値として構成されています。 + +オブジェクトリテラル(`{}`記法)を使用すると、オブジェクトそのものを作る事ができます。この方法で作られたオブジェクトは`Object.prototype`から[継承](#object.prototype)され、[own properties](#object.hasownproperty)が何も設定されてない状態になります。 + + 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`演算子を使う事です。プロパティに`undefined`や`null`をセットしても、プロパティ自身ではなく、*キー*に設定された*値*を削除するだけです。 + + 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 undefined`と`foo 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エンジンに正しく解釈させる為には*文字リテラル*を使って記述する事を推奨します。 + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/ja/object/hasownproperty.md b/external/JavaScript-Garden/doc/ja/object/hasownproperty.md new file mode 100644 index 0000000..7b02305 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/object/hasownproperty.md @@ -0,0 +1,44 @@ +## `hasOwnProperty` + +オブジェクトは*自分自身*と**自分以外**のどちらで定義されたプロパティかを[prototype chain](#object.prototype)のどこかでチェックしなくてはなりません。これは`Object.prototype`から継承される全てのオブジェクトの`hasOwnProperty`メソッドを使う必要があります。 + +> **注意:** この方法はプロパティが`undefined`かどうかを調べるには十分では**無い**方法です。 +> プロパティは、ほとんどのオブジェクトで存在しているはずの物ではありますが、`undefined`が +> 値に設定される事態は起こり得ます。 + +`hasOwnProperty`はJavaScriptで唯一プロトタイプチェーン内を**遡らず**にプロパティを扱う事が出来ます。 + + // 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 + +`hasOwnProperty`だけが、正しく期待した結果を出すでしょう。これはあらゆるオブジェクトのプロパティの繰り返し処理をする時必須の事です。オブジェクト*自身*に定義されておらず、プロトタイプチェーンのどこかには定義されているというプロパティを除外する手段が他に**ありません**。 + +### プロパティとしての`hasOwnProperty` + +JavaScriptはプロパティ名として`hasOwnProperty`を保護して**いません**。;従って、この名前のプロパティを持ったオブジェクトが存在する事がありえます。正しい結果を得る為には*外部*の`hasOwnProperty`を使う必要があります。 + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // 常にfalseを返す + + // 他のオブジェクトのhasOwnPropertyを使い、fooの'this'にセットして呼び出す + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### 終わりに + +オブジェクトのプロパティの存在判定をする時は、`hasOwnProperty`が**唯一**のメソッドになります。 +また、**全て**の[`for in` ループ](#object.forinloop)内で`hasOwnProperty`を使う事を推奨します。 +そうする事により組み込みの[prototypes](#object.prototype)の拡張が原因のエラーを避ける事が出来ます。 + diff --git a/external/JavaScript-Garden/doc/ja/object/prototype.md b/external/JavaScript-Garden/doc/ja/object/prototype.md new file mode 100644 index 0000000..a38ff3f --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/object/prototype.md @@ -0,0 +1,86 @@ +## プロトタイプ + +JavaScriptはクラスベース継承モデルは実装されておらず、この代わりに*プロトタイプ*を用いています。 + +プロトタイプモデルを使っている事が、JavaScriptの弱点の一つになっていると良く考えられがちですが、プロトタイプ継承モデルはクラスベース継承モデルよりパワフルだというのは事実です。この事はちょっとしたものでもクラスベースの継承で実装しようとすると、プロトタイプベースの継承よりも作業が難しくなるという事でも分かります。 + +JavaScriptはプロトタイプベースが採用されている唯一の広範に使用されている基本的なプログラミング言語という現実があるので、プロトタイプベースとクラスベースの違いを時々調整しないとなりません。 + +最初の大きな違いはJavaScriptの継承は*プロトタイプチェーン*と呼ばれるもので実行されているという事です。 + +> **注意:** 単に`Bar.prototype = Foo.prototype`を使った場合、両方のオブジェクトは、 +> **同じ**プロトタイプを共有する事になります。その為、片方のオブジェクトのプロトタイプの変更は +> もう一方のオブジェクトに影響します。殆どの場合、このような影響を及ぼしたく無いと思います。 + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // BarのプロトタイプをFooの新しいインスタンスとしてセットする + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Barを実際のコンストラクタとして確実にする為に代入する + Bar.prototype.constructor = Bar; + + var test = new Bar() // 新しくbarインスタンスを作成 + + // プロトタイプチェーンの結果 + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* その他 */ } + +上記では`test`は`Bar.prototype`と`Foo.prototype`の2つのオブジェクトより継承されます。その為`Foo`の中で設定された`method`関数にアクセスできるようになります。また、`Foo`のプロトタイプとしてのインスタンス**それ自体**の`value`プロパティにもアクセスが可能です。`new Bar()`は`Foo`のインスタンスを新しく作**りません**が、プロトタイプに割り合てられた`Foo`インスタンスを再利用している事は注目に値します。従って全ての`Bar`インスタンスは**同じ**`value`プロパティを共有します。 + +> **注意:** `Bar.prototype = Foo`のような使い方は**しない**で下さい。`Foo`はそのプロトタイプではなく、 +> 関数オブジェクト`Foo`自体を指しているからです。 +> プロトタイプチェーンは`Foo.prototype`ではなく`Function.prototype`まで遡るので、 +> `method`はプロトタイプチェーン上に出現しなくなります。 + +### プロパティ探索 + +オブジェクトのプロパティにアクセスする時には、JavaScriptはプロトタイプチェーンを要求された名前を見つけるまで**遡って**探索します。 + +チェーンの先頭(すなわち`Object.prototype`)に到達した際に、まだ指定されたプロパティが見つからなければ、代わりに[undefined](#core.undefined)という値を返します。 + +### プロトタイププロパティ + +プロトタイププロパティはJavaScriptの中でプロトタイプチェーンを構築する為に使われていますが、**任意**の値を代入する事も可能になっています。しかし、プロトタイプとしてプリミティブが代入された場合は単に無視されるだけです。 + + function Foo() {} + Foo.prototype = 1; // 効果無し + +オブジェクトの代入は上記の例のように動作し、動的にプロトタイプチェーンを作る事ができます。 + +### パフォーマンス + +プロトタイプチェーンの上位にあるプロパティを探索する時間はコードの実行パフォーマンスに重大な悪影響を与えます。特に存在しないプロパティにアクセスしようとすると、プロトタイプチェーンの全てのプロパティを探索してしまいます。 + +また、オブジェクトのプロパティに対して[反復](#object.forinloop)処理をすると、プロトタイプチェーン上の**全て**のプロパティを列挙してしまいます。 + + +### 既存のプロトタイプの拡張 + +元々組み込まれてるプロトタイプや`Object.prototype`を拡張するのは、良くありがちなイケていない実装方法になります。 + +このテクニックは[monkey patching][1]と呼ばれるもので*カプセル化*を壊してしまいます。このテクニックは[Prototype][2]のようなフレームワークにより広まりましたが、*非標準*の機能を持っている組み込み型のオブジェクトの乱立という点でも推奨されません。 + +**唯一**組み込みのプロトタイプを拡張しても良い理由としては、JavaScriptエンジンに将来実装されるであろう機能の移植だけです。 +例えば[`Array.forEach`][3]などが、それに当たります。 + +### 終わりに + +ここまでがプロトタイプベース継承モデルを使って複雑なコードを書く前に**必ず**理解すべき事です。また、プロパティチェーンの長さを観察して、もしパフォーマンスに悪影響を及ぼすのを防ぐ為ならば、これを分割をしなければなりません。さらに組み込みのプロトタイプは新しいJavaScriptの機能と互換性が無い限りは**絶対**に拡張してはいけません。 +[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/external/JavaScript-Garden/doc/ja/other/timeouts.md b/external/JavaScript-Garden/doc/ja/other/timeouts.md new file mode 100644 index 0000000..fd8efcd --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/other/timeouts.md @@ -0,0 +1,125 @@ +### `setTimeout`と`setInterval` + +JavaScriptは非同期なので、`setTimeout`と`setInterval`関数を使ってある関数の実行のスケジュールを決める事が可能です。 + +> **注意点:** タイムアウトはECMAScript標準の一部では**ありません**。 +> これらは[DOM][1]の一部として実装されています。 + + function foo() {} + var id = setTimeout(foo, 1000); // Number > 0を返す + +`setTimeout`が呼ばれた時に、タイムアウトのIDを返し、この先**おおよそ**1000ms以内に実行する`foo`をスケジュールします。`foo`は正確に**1度**だけ実行されます。 + +これは、`setTimeout`関数の呼び出しで指定した遅延時間を正確に間違いなく得られるという**事では決してありません**。コードが実行されているJavaScriptエンジンのタイマー分解能によって決まります。この事実はJavaScriptがシングルスレッドなので、他のスレッドでの実行を妨害してしまう事があるかもしれません。 + +第一パラメーターを渡された関数は*グローバルオブジェクト*によって呼び出されます。これは呼び出された関数の内部で[`this`](#functionis)がまさにこのオブジェクトを参照しているという事になります。 + + function Foo() { + this.value = 42; + this.method = function() { + // これはグローバルオブジェクトを参照しています + console.log(this.value); // undefinedを記録するはずです + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **注意点:** `setTimeout`は**関数オブジェクト**を第一引数に取ります。 +> 良く間違えてしまう使い方として`setTimeout(foo(), 1000)`というものがあります。 +> `foo`と`foo`**以外**の呼び出しに対する**戻り値**としてしまいます。これは、大体において、 +> 関数が`undefined`になる為に表に出ないエラーになるでしょう。`setTimeout`はどんな +> エラーも発生`させません`。 + +### `setInterval`でスタッキングコール + +`setTimeout`は関数を一度だけ実行します。`setInterval` - 名前が示すように - **毎回**`X`ミリ秒毎に関数を実行しますが、この使用は推奨されていません。 + +コードがタイムアウト呼び出しブロックで実行される時に、`setInterval`は指定された関数を呼び出します。これは、特に小さい間隔で、関数の結果をスタックに積む事ができます。 + + function foo(){ + // 1秒おきにブロックの何かを実行 + } + setInterval(foo, 100); + +上記のコードでは、`foo`が1回呼び出されて、1秒ブロックされます。 + +`foo`がコードをブロックしている間、`setInterval`は呼び出される予定を確保しています。`foo`が完了した瞬間に、実行を待っている呼び出しが**10回**以上存在しているでしょう。 + +### ブロッキング可能なコードの取り扱い + +簡単かつ、一番コントロール可能な解決法として、関数自体の中で`setTimeout`を使うという方法があります。 + + function foo(){ + // 1秒ブロックする何か + setTimeout(foo, 100); + } + foo(); + +このカプセル化は`setTimeout`の呼び出しだけでなく、呼び出しのスタッキングを防止してより詳細なコントロールが出来ます。`foo`それ自身が今や、再度実行するかしないかを決める事が出来るのです。 + +### 手動でタイムアウトをクリアする + +タイムアウトとインターバルのクリアは、`clearTimeout`か`clearInterval`に個別のIDを渡せば出来ます。最初に`set`関数を使った場所に依存します。 + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### 全てのタイムアウトをクリアする + +全てのタイムアウトや、インターバルをクリアする組み込みメソッドが無い為、機能的にクリアする為には暴力的な手段を使う必要があります。 + + // "全ての"タイムアウトをクリアする + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +ここまでもまだ、任意の数字を与えられた為に影響を受けないタイムアウトがあるかもしれません。そのため、代わりに全てのタイムアウトのIDを追跡する事が推奨されます。それで個別にクリアされます。 + +### 隠された`eval`の使用 + +`setTimeout`と`setInterval` は、第一引数に文字列を取る事が可能です。この仕様は内部で`eval`を使用する為に、**絶対に**使うべきではありません。 + +> **注意点:** タイムアウト関数はECMAScript標準では制定されて**いない**為、 +> 文字列を引数にした場合に厳密な動作は色々なJavaScript実装により異なります。 +> 例えば、MicrosoftのJScriptは`eval`の代わりに`Function`コンストラクターを +> 使用します。 + + function foo() { + // この先呼ばれる + } + + function bar() { + function foo() { + // 絶対に呼ばれない + } + setTimeout('foo()', 1000); + } + bar(); + +この場合、`eval`は[直接](#core.eval)呼ばれないので、文字列が渡された`setTimeout`は*global scope*で実行されます。よって、`bar`のスコープから`foo`のローカル変数は使われないのです。 + +いずれかのタイムアウト関数によって呼び出される関数に引数を渡すために文字列を使わ**ない**という事は、さらに推奨されています。 + + function foo(a, b, c) {} + + // 絶対にこのように使わない + setTimeout('foo(1,2, 3)', 1000) + + // 匿名関数を代わりに使用する + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **注意点:** `setTimeout(foo, 1000, 1, 2, 3)`のようなシンタックスを使用する事も +> できますが、[メソッド](#function.this)を使用した際に、分かりにくいエラーが起りえるので +> 使用はお勧めしません。 + +### 終りに + +`setTimeout`や`setInterval`のパラメーターに文字列を用いては**いけません**。引数が関数に呼び出される必要がある場合**本当**に悪いコードの明確なサインになります。実際の呼び出しには*匿名関数*を渡すべきです。 + +さらに、`setInterval`の使用はスケジューラーがJavaScriptの実行によってブロックされないので、避けるべきでしょう。 + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/ja/types/casting.md b/external/JavaScript-Garden/doc/ja/types/casting.md new file mode 100644 index 0000000..2493b8e --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/types/casting.md @@ -0,0 +1,60 @@ +## 型変換 + +JavaScriptは*弱い型付け*の言語なので、可能な**限り**に*型強制*が適用されます。 + + // これらはtrueです。 + new Number(10) == 10; // Number.toString()が変換される + // numberに戻る + + 10 == '10'; // StringsがNumberに変換される + 10 == '+10 '; // バカみたいに文字列を追加 + 10 == '010'; // もっともっと + isNaN(null) == false; // nullが0に変換される + // もちろんNaNではないです + + // これらはfalseです + 10 == 010; + 10 == '-10'; + +> **ES5での注意点:** `0`から始まるNumberリテラルは8進数(基数が8)として解釈されます。 +> このような8進数のサポートはECMAScript5のstrict modeでは**削除されました**。 + +上記の自体を避ける為に、[厳密等価演算子](#types.equality)を使用する事を**強く**推奨します。また、これはたくさんある落し穴を避けますが、それでもまだJavaScriptの弱い型付けシステムから発生する色々な課題が残っています。 + +### 組み込み型のコンストラクタ + +`Number`や`String`のような組み込み型のコンストラクタは、`new`キーワードの有無で振る舞いが違ってきます。 + + new Number(10) === 10; // False, ObjectとNumber + Number(10) === 10; // True, NumberとNumber + new Number(10) + 0 === 10; // True, 暗黙の型変換によります + +`Number`のような組み込み型をコンストラクタとして使うと、新しい`Number`オブジェクトが作られますが、`new`キーワードを除外すると`Number`関数がコンバーターのように振る舞います。 + +加えて、リテラルかオブジェトではない値を持っていると、さらに型強制が多くなります。 + +最良のオプションは以下の3つの方法の内、1つで型を**明示**してキャストする事になります。 + +### Stringでキャストする + + '' + 10 === '10'; // true + +空の文字列の付加により値を簡単に文字列にキャストできます。 + +### Numberでキャストする + + +'10' === 10; // true + +**単項**プラスオペレーターを使うと数字にキャストする事が可能です。 + +### Booleanでキャストする + +**not**オペレーターを2回使うと、値はブーリアンに変換できます。 + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true diff --git a/external/JavaScript-Garden/doc/ja/types/equality.md b/external/JavaScript-Garden/doc/ja/types/equality.md new file mode 100644 index 0000000..f681e6c --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/types/equality.md @@ -0,0 +1,58 @@ +## 等価と比較 + +JavaScriptはオブジェクトの値の等価の比較方法を2種類持っています。 + +### 等価演算子 + +等価演算子は2つのイコール記号: `==`から成っています。 + +JavaScriptは*弱い型付け*を特徴としています。これは等価演算子が比較をする際に型付けを**強制**するという意味です。 + + "" == "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 + +上記の表では型強制の結果が表示されています。`==`の使用が一般に悪い習慣とみなされる大きな理由として、変換ルールが複雑な為、バグの追跡が困難になる事が挙げられます。 + +加えて、型強制が行なわれるとパフォーマンスにも影響してしまいます。例えば、文字列は他の数字と比較する前に数値に変換されなければなりません。 + +### 厳密等価演算子 + +厳密等価演算子は**3つ**のイコール記号:`===`で成っています。 + +これはオペランドの間で強制的な型変換が**実行されない**事を除けば、通常の等価演算子と同じように正確に動作します。 + + "" === "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 + +上記の結果は、より明確でコードの早期破損を可能にします。これはある程度までコードを硬化させて、オペランドが別の型の場合にパフォーマンスが向上します。 + +### オブジェクトの比較 + +`==`と`===`は両方とも**等価**演算子とされていますが、そのオペランドの少なくとも一つが`Object`の場合は、両者は異なる動きをします。 + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +これら2つの演算子は**同一性**を比較していているのであって、等価を比較しているわけでは**ありません**。これは、これらの演算子はPythonの`is`演算子やCのポインター比較と同じように、同じオブジェクトの**インスタンス**を比較するという事になります。 + +### 終わりに + +**厳密等価**演算子だけを使用することを特に推奨します。型を強制的に型変換する場合は[explicitly](#types.casting)であるべきで、言語自体の複雑な変換ルールが残っているべきではありません。 + diff --git a/external/JavaScript-Garden/doc/ja/types/instanceof.md b/external/JavaScript-Garden/doc/ja/types/instanceof.md new file mode 100644 index 0000000..82a5b80 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/types/instanceof.md @@ -0,0 +1,31 @@ +## `instanceof`オペレーター + +`instanceof`オペレーターは2つのオペランドのコンストラクタを比較します。これはカスタムで作ったオブジェクトを比較する時にのみ有用です。組み込みの型に使用するのは[typeof operator](#types.typeof)を使用するのと同じくらい意味がありません。 + +### カスタムオブジェクトの比較 + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // これは単に関数オブジェクトFooにBar.prototypeをセットしただけです。 + // しかし、実際のFooのインスタンスではありません。 + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### ネイティブ型で`instanceof`を使用する + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +ここで1つ重要な事は、異なるJavaScriptのコンテキスト(例えば、ブラウザの異なるウィンドウ)を元としたオブジェクトでは、コンストラクタが厳密に同じものでは無い為に`instanceof`は上手く動作しません。 + +### 終わりに + +`instanceof`オペレーターは同じJavaScriptのコンテキストが起源になっているカスタムメイドのオブジェクトを扱う場合**のみ**使うべきです。ちょうど[`typeof`](#types.typeof)オペレーターのように、その他での使用は**避けるべき**です。 diff --git a/external/JavaScript-Garden/doc/ja/types/typeof.md b/external/JavaScript-Garden/doc/ja/types/typeof.md new file mode 100644 index 0000000..a70ac82 --- /dev/null +++ b/external/JavaScript-Garden/doc/ja/types/typeof.md @@ -0,0 +1,71 @@ +## `typeof`演算子 + +`typeof`演算子([`instanceof`](#types.instanceof)も同様です)は恐らくJavaScriptの最大の設計ミスです。**完全に壊れている**存在に近いものです。 + +`instanceof`はまだ限られた用途で使用できますが、`typeof`は本当に使用できる実用的なケースはオブジェクトの型を調べるという起こら**ない**ケース一つしかありません。 + +> **注意点:** `typeof`も関数のような構文で呼ぶ事もできます。(例:`typeof(obj)`) +> これは関数呼び出しではありません。2つのカッコは通常と同じように +> `typeof`演算子のオペランドの値を返す振る舞いをします。 +> `typeof`関数は存在**しません**。 + +### JavaScript の型テーブル + + 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 (Nitro/V8ではfunction) + new RegExp("meow") RegExp object (Nitro/V8ではfunction) + {} Object object + new Object() Object object + +上記のテーブルにおいて*Type*は`typeof`演算子が返す値を参照しています。はっきりと分かるように、この値はどれでも一貫しています。 + +*Class*はオブジェクト内部の`[[Class]]`プロパティの値を参照しています。 + +> **仕様より:** `[[Class]]`の値は以下の文字列のいずれかになります。 +> `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +`[[Class]]`の値を取得する為に、`Object.prototype`メソッドの`toString`を使う事があります。 + +### オブジェクトのクラス + +仕様では`[[Class]]`の値にアクセスするためには`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 + +上記の例では[this](#function.this)の値と共に`Object.prototype.toString`が呼び出され`[[Class]]`の取得されている値がオブジェクトとして設定されます。 + +> **ES5での注意点:** 便宜上、ECMAScript 5では +> `Object.prototype.toString`の +> `null`と`undefined`の返す値は`Object`から +> `Null`と`Undefined`に**変更され**ました。 + +### 未定義変数のテスト + + typeof foo !== 'undefined' + +上記では`foo`が実際に宣言されたかどうかを`ReferenceError`の結果を参照してチェックします。これは`typeof`が唯一実際に役に立つ場合です。 + +### 終わりに + +オブジェクトの型をチェックする為には、`Object.prototype.toString`を使用する事を強くお勧めします。これが唯一信頼できる方法だからです。上述の型テーブルでも分かるように、`typeof`の戻り値は仕様で定義されていないものを返します。よって、実装によって別の結果になる事があります。 + +変数が定義されているかチェックしない限りは、`typeof`は**どんな事をしても**避けるべきです。 diff --git a/external/JavaScript-Garden/doc/ko/array/constructor.md b/external/JavaScript-Garden/doc/ko/array/constructor.md new file mode 100644 index 0000000..0c60a84 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/array/constructor.md @@ -0,0 +1,24 @@ +## `배열` 생성자 + +배열을 만들때 `배열` 생성자에 파라미터를 넣어 만드는 방법은 헷갈릴수있다. 그래서 항상 각 괄호(`[]`) 노테이션을 이용해 배열을 만들 것을 권한다 + + [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'] + +`배열` 생성자에 숫자를 인자로 넣으면 그 숫자 크기 만큼의 빈 `배열`을 반환한다. 즉 배열의 `length`는 그 숫자가 된다. 이때 생성자는 **단지** `length` 프로퍼티에 그 숫자를 할당하기만 하고 `배열`은 실제로 초기화 하지도 않는다. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, 이 인덱스는 초기화되지 않음. + +`for`문을 사용하지 않고 문자열을 더하는 경우에는 length 프로퍼티에 숫자를 할당해주는 기능이 유용할 때도 있다. + + new Array(count + 1).join(stringToRepeat); + +### 결론 + +`배열` 생성자는 가능하면 사용하지 말고, 각 괄호 (`[]`) 노테이션이을 사용하자. 후자가 더 간략하고 명확할 뿐만 아니라 보기도 좋다. diff --git a/external/JavaScript-Garden/doc/ko/array/general.md b/external/JavaScript-Garden/doc/ko/array/general.md new file mode 100644 index 0000000..12f6610 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/array/general.md @@ -0,0 +1,40 @@ +## 배열 순회와 프로퍼티 + +JavaScript에서는 배열(Array)도 객체(Object)지만 객체 순회(Iterate)를 할 때 [`for in`](#object.forinloop)을 사용해서 좋을 게 없다. 실제로 배열을 탐색할때 `for in`문 사용하지 말아야 할 이유가 매우 많다. + +> **Note:** JavaScript의 배열은 *연관 배열(Associative Array)*이 **아니다**. JavaScript는 오직 key/value를 맵핑한 [객체](#object.general)만 있을 뿐이다. 연관 배열은 순서를 보장해주지만 객체는 순서를 보장하지 않는다. + +`for in`은 프로토타입 체인에 있는 프로퍼티를 모두 훑는(enumerate) 데다가 객체 자신의 프로퍼티만 훑으려면 [`hasOwnProperty`](#object.hasownproperty)를 사용해야 하기 때문에 `for`보다 20배 느리다. + +### 배열 순회 + +배열을 순회 할때는 일반적인 `for`문을 사용하는 것이 가장 빠르다. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +이 예제에서 `l = list.length`로 배열의 length 값을 캐시해야 한다는 것을 꼭 기억해야 한다. + +매번 반복할때마다 배열에 있는 `length` 프로퍼티에 접근하는 것은 좀 부담스럽다. 최신 JavaScript 엔진은 이 일을 알아서 처리해주기도 하지만 코드가 늘 새 엔진에서 실행되도록 보장할 방법이 없다. + +실제로 캐시 하지 않으면 성능이 반으로 줄어든다. + +### `length` 프로퍼티 + +`length` 프로퍼티의 *getter*는 단순히 Array 안에 있는 엘리먼트의 개수를 반환하고 *setter*는 배열을 할당한 수만큼 잘라 버린다. + + var arr = [1, 2, 3, 4, 5, 6]; + arr.length = 3; + arr; // [1, 2, 3] + + arr.length = 6; + arr.push(4); + arr; // [1, 2, 3, undefined, undefined, undefined, 4] + +현재 크기보다 더 작은 값을 할당하면 배열을 자른다. 배열의 크기를 증가시키면 드문드문(sparse)한 배열을 생성한다. + +### 결론 + +최적의 성능을 위해서는 `for`문을 사용하고 `length` 프로퍼티 값을 캐시해야 한다. 배열에 `for in`을 사용하면 성능도 떨어지고 버그 나기도 쉽다. diff --git a/external/JavaScript-Garden/doc/ko/core/delete.md b/external/JavaScript-Garden/doc/ko/core/delete.md new file mode 100644 index 0000000..7f008b2 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/core/delete.md @@ -0,0 +1,76 @@ +## `delete` 연산자 + +간단히 말해서 전역 변수와 전역 함수 그리고 `DontDelete` 속성을 가진 자바스크립트 객체는 삭제할 수 없다. + +### Global 코드와 Function 코드 + +전역이나 함수 스코프에 정의한 함수나 변수는 모두 Activation 객체나 전역 객체의 프로퍼티다. 이 프로퍼티는 모두 `DontDelete` 속성을 가진다. 전역이나 함수 코드에 정의한 변수와 함수는 항상 `DontDelete` 프로퍼티로 만들어지기 때문에 삭제될 수 없다: + + // Global 변수: + var a = 1; // DontDelete가 설정된다. + delete a; // false + a; // 1 + + // Function: + function f() {} // DontDelete가 설정된다. + delete f; // false + typeof f; // "function" + + // 다시 할당해도 삭제할 수 없다: + f = 1; + delete f; // false + f; // 1 + +### 명시적인(Explicit) 프로퍼티 + +다음 예제에서 만드는 프로퍼티는 delete할 수 있다. 이런 걸 명시적인(Explicit) 프로퍼티라고 부른다: + + // Explicit 프로퍼티를 만든다: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +`obj.x`와 `obj.y`는 `DontDelete` 속성이 아니라서 delete할 수 있다. 하지만 다음과 같은 코드도 잘 동작하기 때문에 헷갈린다: + + // IE를 빼고 잘 동작한다: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - 진짜 Global 변수인지 확인하는 것 + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +[`this`](#function.this)가 전역 객체를 가리키는 것을 이용해서 명시적으로 프로퍼티 `a`를 선언하면 삭제할 수 있다. 이것은 꼼수다. + +IE (적어도 6-8)는 버그가 있어서 안 된다. + +### Argument들과 Function의 기본 프로퍼티 + +Function의 [`arguments` 객체](#function.arguments)와 기본 프로퍼티도 `DontDelete` 속성이다. + + // Function의 arguments와 프로퍼티: + (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 객체 + +> **역주:** Host 객체는 document같은 DOM 객체를 말한다. + +Host 객체를 delete하면 어떻게 될지 알 수 없다. 표준에는 어떻게 Host 객체를 delete해야 하는지 정의하지 않았다. + +### 결론 + +`delete` 연산자는 엉뚱하게 동작할 때가 많다. 명시적으로 정의한 일반 객체의 프로퍼티만 delete하는 것이 안전하다. diff --git a/external/JavaScript-Garden/doc/ko/core/eval.md b/external/JavaScript-Garden/doc/ko/core/eval.md new file mode 100644 index 0000000..2a97ebe --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/core/eval.md @@ -0,0 +1,38 @@ +## 왜 `eval`을 사용하면 안 될까? + +`eval` 함수는 JavaScript 문자열을 지역 스코프에서 실행한다. + + var number = 1; + function test() { + var number = 2; + eval('number = 3'); + return number; + } + test(); // 3 + number; // 1 + +`eval`함수는 `eval`이라는 이름으로 **직접** 실행할 때에만 지역 스코프에서 실행된다. 그리고 `eval`이라는 이름에 걸맞게 악명또한 높다. + + var number = 1; + function test() { + var number = 2; + var copyOfEval = eval; + copyOfEval('number = 3'); + return number; + } + test(); // 2 + number; // 3 + +어쨌든 `eval`은 사용하지 말아야 한다. eval을 사용하는 99.9%는 사실 eval 없이도 만들수있다. + +### 가짜 `eval` + +[`setTimeout`과 `setInterval`](#other.timeouts)은 첫 번째 인자로 스트링을 입력받을 수 있다. 이 경우에는 `eval`을 직접 호출하는 것이 아니라서 항상 Global Scope에서 실행된다. + +### 보안 이슈 + +`eval`은 어떤 코드라도 **무조건** 실행하기 때문에 보안 문제도 있다. 따라서 신뢰하지 못하거나 모르는 코드가 포함되어 있을 경우 **절대로** 사용해서는 안된다. + +### 결론 + +`eval`은 사용하지 않는 게 좋다. `eval`을 사용하는 모든 코드는 성능, 보안, 버그 문제를 일으킬 수 있다. 만약 `eval`이 필요해지면 *설계를 변경*하여 `eval`이 필요 없게 만들어야 한다. diff --git a/external/JavaScript-Garden/doc/ko/core/semicolon.md b/external/JavaScript-Garden/doc/ko/core/semicolon.md new file mode 100644 index 0000000..7a384ca --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/core/semicolon.md @@ -0,0 +1,99 @@ +## 자동으로 삽입되는 쎄미콜론 + +JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 쎄미콜론을 사용하도록 강제하지는 않는다. 그래서 생략할 수 있다. + +사실 JavaScript는 쎄미콜론이 꼭 있어야 하고 없으면 이해하지 못한다. 그래서 JavaScript 파서는 쎄미콜론이 없으면 **자동으로** 쎄미콜론을 추가한다. + + var foo = function() { + } // 쎄미콜론이 없으니 에러 난다. + test() + +파서는 쎄미콜론을 삽입하고 다시 시도한다. + + var foo = function() { + }; // 에러가 없어짐. + test() + +쎄미콜론을 자동으로 삽입한 것이 **대표적인** JavaScript 설계 오류다. 쎄미콜론 유무에 따라 *전혀* 다른 코드가 될 수 있다. + +### 어떻게 다를까? + +코드에 쎄미콜론이 없으면 파서가 어디에 넣을지 결정한다. + + (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) + +파서는 이 코드에 쎄미콜론을 다음과 같이 삽입한다. + + (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); //<- 여기에 파서는 쎄미콜론을 넣는다. + +> **주의:** JavaScript 파서는 new line 문자가 뒤따라 오는 return 구문을 제대로 처리하지 못한다. 자동으로 쎄미콜론을 넣는 것 자체의 문제는 아니지만 어쨌든 여전히 문제다. + +파서는 완전히 다른 코드로 만들어 버린다. 이것은 **오류**다. + +### 괄호 해석 + +파서는 괄호에는 쎄미콜론을 넣지 않는다. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +그래서 다음과 같이 한줄로 코드를 바꾼다. + + log('testing!')(options.list || []).forEach(function(i) {}) + +이렇게 한줄로 바뀌면 `log` 함수가 함수를 반환할 가능성이 거의 없으므로 `undefined is not a function`이라는 `TypeError`가 발생한다. + +### 결론 + +쎄미콜론은 반드시 사용해야 한다. 그리고 `{}`도 생략하지 않고 꼭 사용하는 것이 좋다. 한 줄밖에 안 되는 `if` / `else` 블럭에서도 꼭 사용해야 한다. 이 두 가지 규칙을 잘 지키면 JavaScript 파서가 잘못 해석하는 일을 미리 방지하고 코드도 튼튼해진다. diff --git a/external/JavaScript-Garden/doc/ko/core/undefined.md b/external/JavaScript-Garden/doc/ko/core/undefined.md new file mode 100644 index 0000000..2bacc83 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/core/undefined.md @@ -0,0 +1,56 @@ +## `undefined`와 `null` + +JavaScript는 `nothing`을 표현할때 `null`과 `undefined` 두 가지로 표현할 수 있고 그중 `undefined`가 더 유용하다. + +### `undefined`도 변수 + +`undefined`는 `undefined`라는 값을 가지는 데이터 형식이다. + +`undefined`는 상수도 아니고 JavaScript의 키워드도 아니다. 그냥 `undefined`라는 이름의 Global 변수이고 이 변수에는 `undefined`라고 할당돼 있다. 그래서 이 Global 변수의 값을 쉽게 바꿀 수 있다. + +> **ES5 Note:** ECMAScript 5의 strict 모드에서는 `undefined`를 더는 바꿀 수 없도록 했다. 하지만 `undefined`라는 함수를 만들면 여전히 할당할 수 있다. + +`undefined` 값이 반환될 때: + + - global 변수 `undefined`에 접근할 때. + - 선언은 했지만 아직 초기화하지 않은 변수에 접근할 때. + - `return` 구문이 없는 함수는 암묵적으로 `undefined`를 반환함. + - `return` 구문으로 아무것도 반환하지 않을 때. + - 없는 프로퍼티를 찾을 때. + - 함수 인자가 생략될 때. + - `undefined`가 할당된 모든 것. + - `void(expression)` 형식으로 된 표현 + +> **역주:** 예를 들어 CoffeeScript Compliler는 CoffeeScript의 `undefined`를 JavaScript의 `void 0`로 컴파일한다. + +### `undefined`가 바뀔 때를 대비하기 + +global 변수 `undefined`는 `undefined`라는 객체를 가리키는 것뿐이기 때문에 새로운 값을 할당한다고 해도 `undefined`의 값 자체가 바뀌는 것이 아니다. + +그래서 `undefined`와 비교하려면 먼저 `undefined`의 값을 찾아와야 한다. + +`undefined` 변수가 바뀔 때를 대비해서 `undefined`라는 변수를 인자로 받는 [anonymous wrapper](#function.scopes)로 감싸고 인자를 넘기지 않는 꼼수를 사용한다. + + var undefined = 123; + (function(something, foo, undefined) { + // Local Scope에 undefined를 만들어서 + // 원래 값을 가리키도록 했다. + + })('Hello World', 42); + +wrapper 안에 변수를 새로 정의하는 방법으로도 같은 효과를 볼 수 있다. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +이 두 방법의 차이는 minified했을 때 4바이트만큼 차이 난다는 것과 한쪽은 wrapper 안에 var 구문이 없다는 것밖에 없다. + +### `Null` 객체의 용도 + +JavaScript 언어에서는 `undefined`를 다른 언어의 *null* 처럼 쓴다. 진짜 `null`은 그냥 데이터 타입 중 하나일 뿐이지 더도덜도 아니다. + +JavaScript를 깊숙히 건드리는 것이 아니면 null 대신 `undefined`를 사용해도 된다(`Foo.prototype = null`같이 프로토타입 체인을 끊을 때는 null을 사용한다). diff --git a/external/JavaScript-Garden/doc/ko/function/arguments.md b/external/JavaScript-Garden/doc/ko/function/arguments.md new file mode 100644 index 0000000..288928f --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/arguments.md @@ -0,0 +1,108 @@ +## `arguments` 객체 + +JavaScript의 모든 함수 스코프에는 `arguments`라는 특별한 변수가 있다. 이 변수는 함수에 넘겨진 모든 인자에 대한 정보가 담겨 있다. + +> **Note:** `arguments` 변수는 Function 안에서 다시 정의할 수 없다. `var` 구문이나 파라미터에 `arguments`라는 이름으로 변수를 정의해도 변수가 재정의되지 않는다. + +`arguments` 객체는 `Array`가 아니다. 물론 `length` 프로퍼티도 있고 여러모로 Array와 비슷하게 생겼지만 Array.prototype을 상속받지는 않았다. + +그래서 `arguments`에는 `push`, `pop`, `slice` 같은 표준 메소드가 없다. 일반 `for`문을 이용해 순회는 할수 있지만, `Array`의 메소드를 이용하려면 `arguments`를 Array로 변환해야 한다. + +### Array로 변환하기 + +다음 코드는 arguments에 있는 객체를 새로운 Array에 담아 반환한다. + + Array.prototype.slice.call(arguments); + +이 변환 과정은 **느리기** 때문에 성능이 중요한 부분에 사용하는 것은 **별로 바람직하지** 못 하다. + +### arguemnts 객체 넘기기 + +어떤 함수에서 다른 함수로 arguments 객체를 넘길 때에는 다음과 같은 방법을 권한다. (역주: foo 함수는 bar 함수 한번 랩핑한 함수다. ) + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // 내곡동에 땅이라도 산다. + } + +또 다른 트릭은 `call`과 `apply`를 함께 사용하여 메소드(`this`의 값과 인자들을 사용하는 함수)를 +단지 인자들만 사용하는 일반 함수로 바꾸는 것이다. + + function Person(first, last) { + this.first = first; + this.last = last; + } + + Person.prototype.fullname = function(joiner, options) { + options = options || { order: "western" }; + var first = options.order === "western" ? this.first : this.last; + var last = options.order === "western" ? this.last : this.first; + return first + (joiner || " ") + last; + }; + + // "fullname" 메소드의 비결합(unbound) 버전을 생성한다. + // 첫번째 인자로 'first'와 'last' 속성을 가지고 있는 어떤 객체도 사용 가능하다. + // "fullname"의 인자 개수나 순서가 변경되더라도 이 랩퍼를 변경할 필요는 없을 것이다. + Person.fullname = function() { + // 결과: Person.prototype.fullname.call(this, joiner, ..., argN); + return Function.call.apply(Person.prototype.fullname, arguments); + }; + + var grace = new Person("Grace", "Hopper"); + + // 'Grace Hopper' + grace.fullname(); + + // 'Turing, Alan' + Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" }); + +### 일반 파라미터와 arguments 객체의 인덱스 + +일반 파라미터와 `arguments` 객체의 프로퍼티는 모두 *getter*와 *setter*를 가진다. + +그래서 파라미터나 `arguments` 객체의 프로퍼티의 값을 바꾸면 둘 다 바뀐다. + + 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); + +### 성능에 대한 오해와 진실. + +`arguments` 객체는 항상 만들어지지만 두가지 예외사항이 있다. `arguments`라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 `arguemnts` 객체는 만들어지지 않는다. 하지만 뭐 이런 경우들은 어차피 arguments 객체를 안쓰겠다는 의미니까 상관 없다. + +그리고 *getter*와 *setter*는 항상 생성되기 때문에 getter/setter를 사용하는 것은 성능에 별 영향을 끼치지 않는다. 예제처럼 단순한 코드가 아니라 `arguments` 객체를 다방면으로 활용하는 실제 코드에서도 마찬가지다. + +> **ES5 Note:** strict 모드에서는 *getter*와 *setter*가 생성되지 않는다. + +그러나 예외도 있다. 최신 JavaScript 엔진에서 `arguments.callee`를 사용하면 성능이 확 떨어진다. + + function foo() { + arguments.callee; // 이 함수를 가리킨다. + arguments.callee.caller; // 이 함수를 호출한 부모함수를 가리킨다. + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // 원래 인라인 돼야 하는디... + } + } + +위 코드에서 'foo' 함수는 자기 자신과 자신을 호출한 함수를 알아야 하기 때문에 더이상 [인라인][1]되지 않는다. 이렇게 쓰면 인라인이 주는 성능상 장점을 포기해야 하는데다가 이 함수가 호출되는 상황(calling context)에 의존하게 돼 버려서 캡슐화(Encapsulation)도 해친다. +(역주: 보통 코드가 컴파일 될때 코드를 인라인 시키면서 최적화 하는데, 위와 같이 arguments.callee나 caller를 사용하게 되면 런타임시에 해당 함수가 결정되므로 인라인 최적화를 할수가 없다.) + +`arguments.callee`와 arguments.callee의 프로퍼티들은 **절대** 사용하지 말자!. + +> **ES5 Note:** strict 모드에서 `arguments.callee`는 deprecated됐기 때문에 사용하면 `TypeError`가 난다. + +[1]: http://en.wikipedia.org/wiki/Inlining diff --git a/external/JavaScript-Garden/doc/ko/function/closures.md b/external/JavaScript-Garden/doc/ko/function/closures.md new file mode 100644 index 0000000..4e5631f --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/closures.md @@ -0,0 +1,95 @@ +## 클로져(Closure)와 참조(Reference) + +*클로져*는 JavaScript의 특장점 중 하나다. 클로저를 만들면 클로저 스코프 안에서 클로저를 만든 외부 스코프(Scope)에 항상 접근할 있다. JavaScript에서 스코프는 [함수 스코프](#function.scopes)밖에 없기 때문에 기본적으로 모든 함수는 클로저가 될수있다. + +### private 변수 만들기 + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +여기서 `Counter`는 `increment` 클로저와 `get` 클로저 두 개를 반환한다. 이 두 클로저는 `Counter` 함수 스코프에 대한 **참조**를 유지하고 있기 때문에 이 함수 스코프에 있는 count 변수에 계속 접근할 수 있다. + +### Private 변수의 동작 원리 + +JavaScript에서는 스코프(Scope)를 어딘가에 할당해두거나 참조할수 없기 때문에 스코프 밖에서는 count 변수에 직접 접근할 수 없다. 접근할수 있는 유일한 방법은 스코프 안에 정의한 두 클로저를 이용하는 방법밖에 없다. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +위 코드에서 `foo.hack` 함수는 Counter 함수 안에서 정의되지 않았기 때문에 이 함수가 실행되더라도 `Counter` 함수 스코프 안에 있는 count 값은 변하지 않는다. 대신 foo.hack 함수의 `count`는 *Global* 스코프에 생성되거나 이미 만들어진 변수를 덮어쓴다. + +### 반복문에서 클로저 사용하기 + +사람들이 반복문에서 클로저를 사용할 때 자주 실수를 하는 부분이 있는데 바로 인덱스 변수를 복사할때 발생한다. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +이 코드는 `0`부터 `9`까지의 수를 출력하지 않고 `10`만 열 번 출력한다. + +타이머에 설정된 *익명* 함수는 변수 `i`에 대한 참조를 들고 있다가 `console.log`가 호출되는 시점에 `i`의 값을 사용한다. `console.log`가 호출되는 시점에서 `for loop`는 이미 끝난 상태기 때문에 `i` 값은 10이 된다. + +기대한 결과를 얻으려면 `i` 값을 복사해 두어야 한다. + +### 앞의 참조 문제 해결하기 + +반복문의 index 값을 복사하는 가장 좋은 방법은 익명함수로 랩핑[Anonymous Wrapper](#function.scopes)하는 방법이다. + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +이 익명 함수에 `i`를 인자로 넘기면 이 함수의 파라미터 e에 i의 **값**이 복사되어 넘어갈 것이다. + +그리고 `setTimeout`는 익명 함수의 파라미터인 `e`에 대한 참조를 갖게 되고 `e`값은 복사되어 넘어왔으므로 loop의 상태에 따라 변하지 않는다. + +또다른 방법으로 랩핑한 익명 함수에서 출력 함수를 반환하는 방법도 있다. 아래 코드는 위 코드와 동일하게 동작한다. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +즐겨 쓰이는 또 하나의 방법은 `setTimeout` 함수에 세번째 인자를 추가하는 방법이다. +추가된 인자는 콜백 함수에 전달된다. + + for(var i = 0; i < 10; i++) { + setTimeout(function(e) { + console.log(e); + }, 1000, i); + } + +레거시 JS 환경(Internet Explorer 9 이하)은 이 방법을 지원하지 않는다. + +`.bind`를 사용하는 방법도 있다. `.bind`는 `this` 컨텍스트와 인자들을 함수에 결속(bind)시킨다. +아래 코드는 위 코드와 동일하게 동작한다. + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } diff --git a/external/JavaScript-Garden/doc/ko/function/constructors.md b/external/JavaScript-Garden/doc/ko/function/constructors.md new file mode 100644 index 0000000..2b26dc2 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/constructors.md @@ -0,0 +1,104 @@ +## 생성자 + +JavaScript의 생성자는 다른 언어들과 다르게 `new` 키워드로 호출되는 함수가 생성자가 된다. + +생성자로 호출된 함수의 this 객체는 새로 생성된 객체를 가리키고, **새로 만든** 객체의 [prototype](#object.prototype)에는 생성자의 prototype이 할당된다. + +그리고 생성자에 명시적인 `return` 구문이 없으면 this가 가리키는 객체를 반환한다. + + function Person(name) { + this.name = name; + } + + Person.prototype.logName = function() { + console.log(this.name); + }; + + var sean = new Person(); + +위 코드는 `Person`을 생성자로 호출하고 새로 생성된 객체의 `prototype`을 `Person.prototype`으로 설정한다. + +아래 코드와 같이 생성자에 명시적인 `return` 문이 있는 경우에는 반환하는 값이 객체인 경우에만 그 값을 반환한다. + + function Car() { + return 'ford'; + } + new Car(); // 'ford'가 아닌 새로운 객체를 반환 + + function Person() { + this.someValue = 2; + + return { + name: 'Charles' + }; + } + new Person(); // someValue가 포함되지 않은 ({name:'Charles'}) 객체 반환 + +new 키워드가 없으면 그 함수는 객체를 반환하지 않는다. + + function Pirate() { + this.hasEyePatch = true; // 전역 객체를 준비! + } + var somePirate = Pirate(); // somePirate = undefined + +위 예제는 그때그때 다르게 동작한다. 그리고 [`this`](#function.this) 객체의 동작 원리에 따라서 Foo 함수안의 `this`의 값은 *Global 객체*를 가리키게된다. +(역주: 결국 new 키워드를 빼고, 코드를 작성할 경우 원치 않은 this 참조 오류가 발생할 수 있다.) + +### 팩토리 + +생성자가 객체를 반환하면 `new` 키워드를 생략할 수 있다. + + function Robot() { + var color = 'gray'; + return { + getColor: function() { + return color; + } + } + } + Robot.prototype = { + someFunction: function() {} + }; + + new Robot(); + Robot(); + +new 키워드의 유무과 관계없이 `Robot` 생성자의 동작은 동일하다. 즉 [클로저](#function.closures)가 할당된 method 프로퍼티가 있는 새로운 객체를 만들어 반환한다. + +`new Robot()`으로 호출되는 생성자는 반환되는 객체의 prototype 프로퍼티에 아무런 영향을 주지 않는다. 객체를 반환하지 않는 생성자로 만들어지는 경우에만 객체의 prototype이 생성자의 것으로 할당된다. + +그러니까 이 예제에서 `new` 키워드의 유무는 아무런 차이가 없다. +(역주: 생성자에 객체를 만들어 명시적으로 반환하면 new 키워드에 관계없이 잘 동작하는 생성자를 만들수있다. 즉, new 키워드가 빠졌을때 발생하는 this 참조 오류를 방어해준다.) + +### 팩토리로 객체 만들기 + +`new` 키워드를 빼먹었을 때 버그가 생긴다는 이유로 **아예 new를 사용하지 말 것**을 권하기도 한다. + +객체를 만들고 반환해주는 팩토리를 사용하여 `new` 키워드 문제를 회피할 수 있다. + + function CarFactory() { + var car = {}; + car.owner = 'nobody'; + + var milesPerGallon = 2; + + car.setOwner = function(newOwner) { + this.owner = newOwner; + } + + car.getMPG = function() { + return milesPerGallon; + } + + return car; + } + +`new` 키워드가 없어도 잘 동작하고 [private 변수](#function.closures)를 사용하기도 쉽다. 그렇지만, 단점도 있다. + + 1. prototype으로 메소드를 공유하지 않으므로 메모리를 좀 더 사용한다. + 2. 팩토리를 상속하려면 모든 메소드를 복사하거나 객체의 prototype에 객체를 할당해 주어야 한다. + 3. `new` 키워드를 누락시켜서 prototype chain을 끊어버리는 것은 아무래도 언어의 의도에 어긋난다. + +### 결론 + +`new` 키워드가 생략되면 버그가 생길 수 있지만 그렇다고 prototype을 사용하지 않을 이유가 되지 않는다. 애플리케이션에 맞는 방법을 선택하는 것이 나을 거고 어떤 방법이든 **엄격하고 한결같이* 지켜야 한다. diff --git a/external/JavaScript-Garden/doc/ko/function/general.md b/external/JavaScript-Garden/doc/ko/function/general.md new file mode 100644 index 0000000..091fd10 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/general.md @@ -0,0 +1,37 @@ +## 함수 선언과 함수 표현식 + +JavaScript에서 함수는 First Class Object다. 즉, 함수 자체가 또 다른 함수의 인자될 수 있다는 말이다. 그래서 익명 함수를 비동기 함수의 콜백으로 넘기는 것도 이런 특징을 이용한 일반적인 사용법이다. + +### `함수` 선언 + + function foo() {} + +위와 같이 선언한 함수는 프로그램이 실행하기 전에 먼저 [호이스트(Hoist)](#function.scopes) (스코프가 생성)되기 때문에 정의된 스코프(Scope) 안에서는 어디서든 이 함수를 사용할 수 있다. 심지어 함수를 정의하기 전에 호출해도 된다. + + foo(); // 이 코드가 실행되기 전에 foo가 만들어지므로 잘 동작한다. + function foo() {} + +### `함수` 표현식 + + var foo = function() {}; + +위 예제는 `foo` 변수에 *익명* 함수를 할당한다. + + foo; // 'undefined' + foo(); // TypeError가 난다. + var foo = function() {}; + +'var'문을 이용해 선언하는 경우, 코드가 실행되기 전에 'foo' 라는 이름의 변수를 스코프의 맨 위로 올리게 된다.(호이스트 된다) 이때 foo 값은 undefiend로 정의된다. + +하지만 변수에 값을 할당하는 일은 런타임 상황에서 이루어지게 되므로 실제 코드가 실행되는 순간의 `foo`변수는 기본 값인 [undefined](#core.undefined)이 된다. + +### 이름있는 함수 표현식 + +이름있는 함수를 할당할때도 특이한 경우가 있다. + + var foo = function bar() { + bar(); // 이 경우는 동작 하지만, + } + bar(); // 이 경우는 참조에러를 발생시킨다. + +foo 함수 스코프 밖에서는 foo 변수 외에는 다른 값이 없기 때문에 `bar`는 함수 밖에서 사용할 수 없지만 함수 안에서는 사용할 수 있다. [이와 같은 방법](#function.scopes)으로 자바스크립트에서 어떤 함수의 이름은 항상 그 함수의 지역 스코프 안에서 사용할수있다. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/ko/function/scopes.md b/external/JavaScript-Garden/doc/ko/function/scopes.md new file mode 100644 index 0000000..b94dc10 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/scopes.md @@ -0,0 +1,191 @@ +## 스코프와 네임스페이스 + +JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처리하지만, Block Scope은 지원하지 않는다. 그래서 JavaScript에서는 항상 *함수 스코프*를 사용한다. + + function test() { // Scope + for(var i = 0; i < 10; i++) { // Scope이 아님 + // count + } + console.log(i); // 10 + } + +> **Note:** 할당할 때, 반환할 때, Function 인자에서 사용되는 것을 제외하면 `{...}`는 모두 객체 리터럴이 아니라 Block 구문으로 해석된다. 그래서 [세미콜론을 자동으로 넣어주면](#core.semicolon) 에러가 생길 수 있다. + +그리고 JavaScript에는 Namepspace 개념이 없기 때문에 모든 값이 하나의 *전역* 스코프에 정의된다. + +변수를 참조 할 때마다 JavaScript는 해당 변수를 찾을 때까지 상위 방향으로 스코프를 탐색한다. 변수 탐색하다가 전역 스코프에서도 찾지 못하면 `ReferenceError`를 발생시킨다. + +### 전역 변수 문제. + + // script A + foo = '42'; + + // script B + var foo = '42' + +이 두 스크립트는 전혀 다르다. Script A는 *전역* 스코프에 `foo`라는 변수를 정의하는 것이고 Script B는 *현* 스코프에 변수 `foo`를 정의하는 것이다. + +다시 말하지만, 이 둘은 전혀 다르고 `var`가 없을 때 특별한 의미가 있다. + + // Global Scope + var foo = 42; + function test() { + // local Scope + foo = 21; + } + test(); + foo; // 21 + +test 함수 안에 있는 'foo' 변수에 `var` 구문을 빼버리면 Global Scope의 `foo`의 값을 바꿔버린다. '뭐 이게 뭐가 문제야'라고 생각될 수 있지만 수천 줄인 JavaScript 코드에서 `var`를 빼먹어서 생긴 버그를 해결하는 것은 정말 어렵다. + + // 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++) { // var가 없다. + // 내가 for문도 해봐서 아는데... + } + } + +subLoop 함수는 전역 변수 `i`의 값을 변경해버리기 때문에 외부에 있는 for문은 `subLoop`을 한번 호출하고 나면 종료된다. 두 번째 `for`문에 `var`를 사용하여 `i`를 정의하면 이 문제는 생기지 않는다. 즉, 의도적으로 외부 스코프의 변수를 사용하는 것이 아니라면 `var`를 꼭 넣어야 한다. + +### 지역 변수 + +JavaScript에서 지역 변수는 [함수의 파라미터](#function.general)와 `var`로 정의한 변수밖에 없다. + + // 전역 공간 + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // test 함수의 지역 공간 + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` 변수와 `i` 변수는 `test`함수 스코프에 있는 지역 변수라서 전역 공간에 있는 `foo`, `i` 값은 바뀌지 않는다. 하지만 `bar`는 전역 변수이기 때문에 전역 공간에 있는 `bar`의 값이 변경된다. + +### 호이스팅(Hoisting) + +JavaScript는 선언문을 모두 **호이스트(Hoist)**한다. 호이스트란 `var` 구문이나 `function` 선언문을 해당 스코프의 맨 위로 옮기는 것을 말한다. + + 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]; + } + } + +코드를 본격적으로 실행하기 전에 JavaScript는 `var` 구문과 `function` 선언문을 해당 스코프의 맨위로 옮긴다. + + // var 구문이 여기로 옮겨짐. + var bar, someValue; // default to 'undefined' + + // function 선언문도 여기로 옮겨짐 + function test(data) { + var goo, i, e; // Block Scope은 없으므로 local 변수들은 여기로 옮겨짐 + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // bar()가 아직 'undefined'이기 때문에 TypeError가 남 + someValue = 42; // Hoisting은 할당문은 옮기지 않는다. + bar = function() {}; + + test(); + +블록 스코프(Block Scope)는 없으므로 for문과 if문 안에 있는 `var` 구문들까지도 모두 함수 스코프 앞쪽으로 옮겨진다. 그래서 `if` Block의 결과는 좀 이상해진다. + +원래 코드에서 `if` Block은 *전역 변수* `goo`를 바꾸는 것처럼 보였지만 호이스팅(Hoisting) 후에는 *지역 변수*를 바꾼다. + +*호이스팅*을 모르면 다음과 같은 코드는 `ReferenceError`를 낼 것으로 생각할 것이다. + + // SomeImportantThing이 초기화됐는지 검사한다. + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +`var` 구문은 *전역 스코프*의 맨위로 옮겨지기 때문에 이 코드는 잘 동작한다. + + var SomeImportantThing; + + // SomeImportantThing을 여기서 초기화하거나 말거나... + + // SomeImportantThing는 선언돼 있다. + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### 이름 찾는 순서 + +JavaScript의 모든 Scope은 *현 객체*를 가리키는 [`this`](#function.this)를 가지고 있다. *전역 스코프*에도 this가 있다. + +함수 스코프에는 [`arguments`](#function.arguments)라는 변수가 하나 더 있다. 이 변수는 함수에 인자로 넘겨진 값들이 담겨 있다. + +예를 들어 함수 스코프에서 `foo`라는 변수에 접근할 때 JavaScript는 다음과 같은 순서로 찾는다. + + 1. 해당 Scope에서 `var foo` 구문으로 선언된 것을 찾는다. + 2. Function 파라미터에서 `foo`라는 것을 찾는다. + 3. 해당 Function 이름이 `foo`인지 찾는다. + 4. 상위 Scope으로 있는지 확인하고 있으면 **#1**부터 다시 한다. + +> **Note:** `arguments`라는 파라미터가 있으면 Function의 기본 객체인 `arguments`가 생성되지 않는다. + +### 네임스페이스 + +JavaScript에서는 전역 공간(Namepspace) 하나밖에 없어서 변수 이름이 중복되기 쉽다. 하지만 *이름없는 랩퍼(Anonymous Wrappers)*를 통해 쉽게 피해갈 수 있다. + + (function() { + // 일종의 네임스페이스라고 할 수 있다. + + window.foo = function() { + // 이 클로저는 전역 스코프에 노출된다. + }; + + })(); // 함수를 정의하자마자 실행한다. + +이름없는 함수는 [표현식(expressions)](#function.general)이기 때문에 호출되려면 먼저 평가(Evaluate)돼야 한다. + + ( // 소괄호 안에 있는 것을 먼저 평가한다. + function() {} + ) // 그리고 함수 객체를 반환한다. + () // 평가된 결과를 호출한다. + +함수를 평가하고 바로 호출하는 방법이 몇가지 더 있다. 문법은 다르지만 똑같다. + + // 함수를 평가하자마자 호출하는 방법들... + !function(){}(); + +function(){}(); + (function(){}()); + // 등등... + +### 결론 + +코드를 캡슐화할 때는 항상 *이름없는 랩퍼(Anonymous Wrapper)*로 네임스페이스를 만들어 사용할 것을 추천한다. 이 래퍼(Wrapper)는 이름이 중복되는 것을 막아 주고 더 쉽게 모듈화할 수 있도록 해준다. + +그리고 전역 변수를 사용하는 것은 좋지 못한 습관이다. 이유야 어쨌든 에러 나기 쉽고 관리하기도 어렵다. diff --git a/external/JavaScript-Garden/doc/ko/function/this.md b/external/JavaScript-Garden/doc/ko/function/this.md new file mode 100644 index 0000000..0ad51ad --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/function/this.md @@ -0,0 +1,99 @@ +## `this`의 동작 원리 + +다른 프로그래밍 언어에서 `this`가 가리키는 것과 JavaScript에서 `this`가 가리키는 것과는 좀 다르다. `this`가 가리킬 수 있는 객체는 정확히 5종류나 된다. + +### Global Scope에서 + + this; + +Global Scope에서도 this가 사용될 수 있고 이때에는 *Global* 객체를 가리킨다. + +### 함수를 호출할 때 + + foo(); + +이때에도 `this`는 *Global* 객체를 가리킨다. + +> **ES5 Note:** strict 모드에서 더는 Global 객체를 가리키지 않고 대신 `undefined`를 가리킨다. + +### 메소드로 호출할 때 + + test.foo(); + +이 경우에는 `this`가 `test`를 가리킨다. + +### 생성자를 호출할 때 + + new foo(); + +`new` 키워드로 [생성자](#function.constructors)를 실행시키는 경우에 이 생성자 안에서 `this`는 새로 만들어진 객체를 가리킨다. + +### `this`가 가리키는 객체 정해주기. + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // a = 1, b = 2, c = 3으로 넘어간다. + foo.call(bar, 1, 2, 3); // 이것도... + +`Function.prototype`의 `call`이나 `apply` 메소드를 호출하면 `this`가 무엇을 가리킬지 *정해줄 수 있다*. 호출할 때 첫 번째 인자로 `this`가 가리켜야 할 객체를 넘겨준다. + +그래서 `foo` Function 안에서 `this`는 위에서 설명했던 객체 중 하나를 가리키는 것이 아니라 `bar`를 가리킨다. + +> **Note:** 객체 리터럴에서 this는 그 객체를 가리키지 않는다. 예를 들어 `var obj= {me:this}`에서 `me`가 `obj`를 가리키는 것이 아니라 위에 설명한 5가지 객체 중 하나를 가리킨다. + +### 대표적인 함정 + +`this`가 Global 객체를 가리키는 것도 잘못 설계된 부분 중 하나다. 괜찮아 보이지만 실제로는 전혀 사용하지 않는다. + + Foo.method = function() { + function test() { + // 여기에서 this는 Global 객체를 가리킨다. + } + test(); + }; + +`test` 에서 `this`가 `Foo`를 가리킬 것으로 생각할 테지만 틀렸다. 실제로는 그렇지 않다. + +`test`에서 `Foo`에 접근하려면 method에 Local 변수를 하나 만들고 `Foo`를 가리키게 하여야 한다. + + Foo.method = function() { + var self = this; + function test() { + // 여기에서 this 대신에 self를 사용하여 Foo에 접근한다 + } + test(); + }; + +`self`는 통상적인 변수 이름이지만, 바깥쪽의 `this`를 참조하기 위해 일반적으로 사용된다. +또한 [클로저](#function.closures)와 결합하여 `this`의 값을 주고 받는 용도로 사용할 수도 있다. + +ECMAScript 5부터는 익명 함수와 결합된 `bind` 메소드를 사용하여 같은 결과를 얻을 수 있다. + + Foo.method = function() { + var test = function() { + // this는 이제 Foo를 참조한다 + }.bind(this); + test(); + }; + +### Method 할당하기 + +JavaScript의 또다른 함정은 바로 함수의 별칭을 만들수 없다는 점이다. 별칭을 만들기 위해 메소드를 변수에 넣으면 자바스크립트는 별칭을 만들지 않고 바로 *할당*해 버린다. + + var test = someObject.methodTest; + test(); + +첫번째 코드로 인해 이제 `test`는 다른 함수와 똑같이 동작한다. 그래서 test 함수 내부의 `this`도 더이상 someObject를 가리키지 않는다. (역주: test가 methodTest의 별칭이라면 methodTest 함수 내부의 this도 someObject를 똑같이 가리켜야 하지만 test의 this는 더이상 someObject가 아니다.) + +이렇게 `this`를 늦게 바인딩해서 나타나는 약점때문에 늦은 바인딩이 나쁜 거라고 생각할수도 있지만, 사실 이런 특징으로 인해 [프로토타입 상속(prototypal inheritance)](#object.prototype)도 가능해진다. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +`Bar` 인스턴스에서 `method`를 호출하면 `method`에서 `this`는 바로 그 인스턴스를 가리킨다. diff --git a/external/JavaScript-Garden/doc/ko/index.json b/external/JavaScript-Garden/doc/ko/index.json new file mode 100644 index 0000000..6d22a89 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden", + "description": "A Guide to JavaScript's Quirks and Flaws.", + "sections": [ + { + "title": "소개", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "객체", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "함수", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Array", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "타입", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "핵심", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "기타", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/ko/intro/index.md b/external/JavaScript-Garden/doc/ko/intro/index.md new file mode 100644 index 0000000..ea8da0c --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/intro/index.md @@ -0,0 +1,41 @@ +## Intro + +JavaScript 언어의 핵심에 대한 내용을 모아 **JavaScript Garden**을 만들어 었다. 이 글이 초보자가 JavaScript 익히면서 자주 겪는 실수, 미묘한 버그, 성능 이슈, 나쁜 습관들 줄일 수 있도록 도와줄 것이다. + +JavaScript Garden은 단순히 JavaScript 언어 자체를 설명하려 만들지 않았다. 그래서 이 글에서 설명하는 주제들을 이해하려면 반드시 언어에 대한 기본 지식이 필요하다. 먼저 Mozilla Developer Network에 있는 [문서][1]로 JavaScript 언어를 공부하기 바란다. + +## 저자들 + +이 글은 [Stack Overflow][2]에서 사랑받는 두 사람 [Ivo Wetzel][3]과 [Zhang Yi Jiang][4]의 작품이다. Ivo Wetzel이 글을 썼고 Zhang Yi jiang이 디자인을 맡았다. + +## 기여자들 + +- [기여자들](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## 번역 + - [박창우][] + - [손병대][] + +[박창우]: https://github.com/pismute +[손병대]: https://github.com/miconblog + +## 호스팅 + +JavaScript Garden은 Github에서 호스팅하고 있고 [Cramer Development][7]가 [JavaScriptGarden.info][8]에서 미러링해주고 있다. + +## 저작권 + +JavaScript Garden은 [MIT license][9]를 따르고 [GitHub][10]에서 호스팅하고 있다. 문제를 발견하면 [이슈를 보고][11]하거나 수정해서 Pull Request를 하라. 아니면 Stack Overflow 채팅 사이트의 [Javascript room][12]에서 우리를 찾으라. + +[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/external/JavaScript-Garden/doc/ko/object/forinloop.md b/external/JavaScript-Garden/doc/ko/object/forinloop.md new file mode 100644 index 0000000..fd05ced --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/object/forinloop.md @@ -0,0 +1,36 @@ +## `for in` Loop + +객체의 프로퍼티를 탐색할때 `in` 연산자와 마찬가지로 `for in` 문도 프로토타입 체인까지 탐색한다. + +> **Note:** `for in`문은 배열의 `length`프로퍼티처럼 `enumerable` 속성이 `false`인 프로퍼티는 탐색하지 않는다. + + // Object.prototype을 오염시킨다. + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // bar와 moo 둘 다 출력한다. + } + +`for in`문에 정의된 기본 동작을 바꿀순 없기 때문에 루프 안에서 불필요한 프로퍼티를 필터링 해야한다. 그래서 `Object.prototype`의 [`hasOwnProperty`](#object.hasownproperty)메소드를 이용해 본래 객체의 프로퍼티만 골라낸다. + +> **Note:** `for in`은 프로토타입 체인을 모두 탐색하기 때문에 상속할 때마다 더 느려진다. + +### `hasOwnProperty`로 필터링 하기 + + // 위의 예제에 이어서 + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +위와 같이 사용해야 올바른 사용법이다. `hasOwnProperty` 때문에 **오직** `moo`만 출력된다. `hasOwnProperty`가 없으면 이 코드는 `Object.prototype`으로 네이티브 객체가 확장될 때 에러가 발생할 수 있다. + +따라서 [Proptotype 라이브러리][1]처럼 네이티브 객체를 프로토타입으로 확장한 프레임워크를 사용할 경우 `for in` 문에 `hasOwnProperty`를 사용하지 않을 경우 문제가 발생할 수 있다. + +### 결론 + +`hasOwnProperty`를 항상 사용하길 권한다. 실제 코드가 동작하는 환경에서는 절대로 네이티브 객체가 프로토타입으로 확장됐다 혹은 확장되지 않았다를 가정하면 안된다. + +[1]: http://www.prototypejs.org/ diff --git a/external/JavaScript-Garden/doc/ko/object/general.md b/external/JavaScript-Garden/doc/ko/object/general.md new file mode 100644 index 0000000..55041eb --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/object/general.md @@ -0,0 +1,82 @@ +## 객체와 프로퍼티 + +JavaScript에서 [`null`](#core.undefined)과 [`undefined`](#core.undefined)를 제외한 모든 것들은 객체처럼 동작한다. + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +숫자 리터럴은 객체처럼 사용되지 못할꺼라는 오해가 있는데 이것은 단지 JavaScript 파서의 문제일 뿐이다. JavaScript 파서는 숫자에 *Dot Notation*이 들어가면 오류라고 생각한다. + + 2.toString(); // SyntaxError가 난다. + +하지만, 숫자를 객체처럼 사용할수 있는 꼼수가 몇 가지 있다. + + 2..toString(); // 두 번째 점은 잘 된다. + 2 .toString(); // 왼쪽 공백이 있으면 잘 된다. + (2).toString(); // 2를 먼저 해석한다. + +### Object 타입 + +JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때문에 [*Hashmap*][1]처럼 사용될 수도 있다. + +객체 리터럴인 Object Notation으로 객체를 만들면 `Object.prototype`을 상속받고 [프로퍼티를 하나도 가지지 않은](#object.hasownproperty) 객체가 만들어진다. + + var foo = {}; // 깨끗한 새 객체를 만든다. + + // 값이 12인 'test' 프로퍼티가 있는 객체를 만든다. + var bar = {test: 12}; + +### 프로퍼티 접근 + +객체의 프로퍼티는 객체이름 다음에 점을 찍어(Dot Notation) 접근하거나 각괄호를 이용해(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 + +두 방식 모두 거의 동일하게 동작한다. 다만 차이가 있다면 각괄호 방식은 프로퍼티 이름을 동적으로 할당해서 값에 접근 할수 있지만 점을 이용한 방식은 구문 오류를 발생시킨다. + +### 프로퍼티 삭제 + +객체의 프로퍼티를 삭제하려면 `delete`를 사용해야만 한다. 프로퍼티에 `undefined`나 `null`을 할당하는 것은 프로퍼티를 삭제하는 것이 아니라 프로퍼티에 할당된 *value*만 지우고 *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]); + } + } + + +위 코드의 출력 결과는 `baz`만 제거했기 때문에 `bar undefined`와 `foo null`은 출력되고 `baz`와 관련된 것은 출력되지 않는다. + +### Notation of Keys + + var test = { + 'case': 'I am a keyword, so I must be notated as a string', + delete: 'I am a keyword, so me too' // SyntaxError가 난다. + }; + +프로퍼티는 따옴표 없는 문자열(plain characters)과 따옴표로 감싼 문자열(strings)을 모두 Key 값으로 사용할 수 있다. 하지만 위와 같은 코드는 JavaScript 파서의 잘못된 설계 때문에 구버전(ECMAScript 5 이전 버전)에서는 `SystaxError`가 발생할 것이다. + +위 코드에서 문제가 되는 `delete` 키워드를 따옴표로 감싸면 구버전의 JavaScript 엔진에서도 제대로 해석될 것이다. + +[1]: http://en.wikipedia.org/wiki/Hashmap diff --git a/external/JavaScript-Garden/doc/ko/object/hasownproperty.md b/external/JavaScript-Garden/doc/ko/object/hasownproperty.md new file mode 100644 index 0000000..fa36645 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/object/hasownproperty.md @@ -0,0 +1,43 @@ +## `hasOwnProperty` + +어떤 객체의 프로퍼티가 자기 자신의 프로퍼티인지 아니면 [프로토타입 체인](#object.prototype)에 있는 것인지 확인하려면 `hasOwnProperty` 메소드를 사용한다. 그리고 이 메소드는 `Object.prototype`으로 부터 상속받아 모든 객체가 가지고 있다. + +> **Note:** hasOwnProperty 메소드로는 어떤 프로퍼티가 존재하는지 확인하는 용도로는 사용할수 있지만, 그 값이 `undefined`일 수 있기 때문에 어떤 프로퍼티의 값이 `undefined`인지 확인하는 용도로 사용하긴 어렵다. + +`hasOwnProperty`메소드는 프로토타입 체인을 탐색하지 않고, 프로퍼티를 다룰수있는 유일한 방법이다. + + // 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 + +`hasOwnProperty` 메소드는 어떤 프로퍼티가 자기 자신의 프로퍼티인지 아닌지 정확하게 알려주기 때문에 객체의 프로퍼티를 순회할때 꼭 필요하다. 그리고 프로토타입 체인 어딘가에 정의된 프로퍼티만을 제외하는 방법은 없다. + +### `hasOwnProperty` 메소드도 프로퍼티다 + +JavaScript는 `hasOwnProperty`라는 이름으로 프로퍼티를 덮어 쓸수도 있다. 그래서 객체 안에 같은 이름으로 정의된 `hasOwnProperty`가 있을 경우, 본래 `hasOwnProperty`의 값을 정확하게 얻고 싶다면 다른 객체의 `hasOwnProperty` 메소드를 빌려써야 한다. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // 항상 false를 반환한다. + + // 다른 객체의 hasOwnProperty를 사용하여 foo 객체의 프로퍼티 유무를 확인한다. + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // Object에 있는 hasOwnProperty를 사용해도 된다. + Object.prototype.hasOwnProperty.call(obj, 'bar'); // true + + +### 결론 + +어떤 객체에 원하는 프로퍼티가 있는지 확인하는 가장 확실한 방법은 `hasOwnProperty`를 사용하는 것이다. [`for in` loop](#object.forinloop)에서 네이티브 객체에서 확장된 프로퍼티를 제외하고 순회하려면 `hasOwnProperty`와 함께 사용하길 권한다. diff --git a/external/JavaScript-Garden/doc/ko/object/prototype.md b/external/JavaScript-Garden/doc/ko/object/prototype.md new file mode 100644 index 0000000..606d810 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/object/prototype.md @@ -0,0 +1,79 @@ +## Prototype + +Javascript는 클래스 스타일의 상속 모델을 사용하지 않고 *프로토타입* 스타일의 상속 모델을 사용한다. + +'이 점이 JavaScript의 약점이다.'라고 말하는 사람들도 있지만 실제로는 prototypal inheritance 모델이 훨씬 더 강력하다. 그 이유는 프로토타입 모델에서 클래스 모델을 흉내 내기는 매우 쉽지만, 반대로 클래스 모델에서 프로토타입 모델을 흉내 내기란 매우 어렵기 때문이다. + +실제로 Prototypal Inheritance 모델을 채용한 언어 중에서 JavaScript만큼 널리 사용된 언어가 없었기 때문에 두 모델의 차이점이 다소 늦게 정리된 감이 있다. + +먼저 가장 큰 차이점은 *프로토타입 체인*이라는 것을 이용해 상속을 구현한다는 점이다. + +> **Note:** 간단히 말해서 `Bar.prototype = Foo.prototype`은 두 객체가 **하나의 프로토타입**을 공유하는 것이다. 그래서 한 객체의 프로토타입을 변경하면 그 프로토타입 객체를 사용하는 다른 객체도 영향을 받는다. 따라서 대부분의 경우 프로토타입을 변경하지는 않는다. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Foo의 인스턴스를 만들어 Bar의 prototype에 할당한다. + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Bar 함수를 생성자로 만들고 + Bar.prototype.constructor = Bar; + + var test = new Bar() // bar 인스턴스를 만든다. + + // 결과적으로 만들어진 프로토타입 체인은 다음과 같다. + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +위 코드에서 `test` 객체는 `Bar.prototype`과 `Foo.prototype`을 둘 다 상속받았기 때문에 Foo에 정의한 `method` 함수에 접근할 수 있다. 그리고 프로토타입 체인에 있는 `Foo` 인스턴스의 `value` 프로퍼티도 사용할 수 있다. `new Bar()`를 해도 `Foo` 인스턴스는 새로 만들어지지 않고 Bar의 prototype에 있는 것을 재사용한다. 그래서 모든 Bar 인스턴스는 **같은** `value` 프로퍼티를 공유한다. + +> **Note:** `Bar.prototype = Foo`라고 하는 것은 `Foo`의 prototype을 가리키는 것이 아니라 Foo라는 Function의 prototype을 가리키는 것이다. 그래서 프로토타입 체인에 `Foo.prototype` 대신 `Function.prototype`이 들어가 있기 때문에 `method` 프로퍼티는 찾지 못한다. + +### 프로토타입 탐색 + +객체의 프로퍼티에 접근하려고 하면 JavaScript는 해당 이름의 프로퍼티를 찾을 때까지 프로토타입 체인을 거슬러 올라가면서 탐색하게 된다. + +프로토타입 체인을 끝까지 탐색했음에도(보통은 `Object.prototype`임) 불구하고 원하는 프로퍼티를 찾지 못하면 [undefined](#core.undefined)를 반환한다. + +### prototype 프로퍼티 + +prototype 프로퍼티는 프로토타입 체인을 만드는 데 사용하고 어떤 값이든 할당할 수 있지만, primitive 값을 할당되면 무시한다. + + function Foo() {} + Foo.prototype = 1; // 무시됨 + +반면에 위 예제처럼 객체를 할당하면 프로토타입 체인이 동적으로 잘 만들어진다. + +### 성능 + +프로토타입 체인을 탐색하는 시간이 오래걸릴수록 성능에 부정적인 영향을 줄수있다. 특히 성능이 중요한 코드에서 프로퍼티 탐색시간은 치명적인 문제가 될수있다. 가령, 없는 프로퍼티에 접근하려고 하면 항상 프로토타입 체인 전체를 탐색하게 된다. + +뿐만아니라 객체를 [순회(Iterate)](#object.forinloop)할때도 프로토타입 체인에 있는 **모든** 프로퍼티를 탐색하게 된다. + +### 네이티브 프로토타입의 확장 + +종종 `Object.prototype`을 이용해 내장 객체를 확장하는 경우가 있는데, 이것도 역시 잘못 설계된 것중에 하나다. + +위와 같이 확장하는 것을 [Monkey Patching][1]라고 부르는데 *캡슐화*를 망친다. 물론 [Prototype][2]같은 유명한 프레임워크들도 이런 확장을 사용하지만, 기본 타입에 표준도 아닌 기능들을 너저분하게 추가하는 이유를 여전히 설명하지 못하고 있다. + +기본 타입을 확장해야하는 유일한 이유는 [`Array.forEach`][3]같이 새로운 JavaScript 엔진에 추가된 기능을 대비해 미리 만들어 놓는 경우 말고는 없다. + +### 결론 + +프로토타입을 이용해 복잡한 코드를 작성하기 전에 반드시 프로토타입 상속 (Prototypal Inheritance) 모델을 완벽하게 이해하고 있어야 한다. 뿐만아니라 프로토타입 체인과 관련된 성능 문제로 고생하지 않으려면 프로토타입 체인이 너무 길지 않도록 항상 주의하고 적당히 끊어줘야 한다. 마지막으로 새로운 JavaScript 기능에 대한 호환성 유지 목적이 아니라면 절대로 네이티브 프로토타입을 확장하지마라. + +[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/external/JavaScript-Garden/doc/ko/other/timeouts.md b/external/JavaScript-Garden/doc/ko/other/timeouts.md new file mode 100644 index 0000000..b41df30 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/other/timeouts.md @@ -0,0 +1,123 @@ +### `setTimeout`과 `setInterval` + +JavaScript는 `setTimeout`과 `setInterval`함수를 이용해 비동기로 함수를 실행시킬수있다. + +> **Note:** Timeout은 ECMAScript 표준이 아니라 [DOM][1]때문에 구현됐다. + + function foo() {} + var id = setTimeout(foo, 1000); // 0보다 큰 수를 반환한다. + +`setTimeout`을 호출하면 타이머의 ID를 반환하고 **대략** 1,000밀리 초 후에 `foo`를 실행시킨다. `foo`는 **딱 한 번만** 실행한다. + +JS엔진은 타이머에 설정한 시간(timer resolution)에 따라서 코드를 실행하지만 단일 쓰레드이기 때문에 특정 코드는 실행이 지연 될수도 있다. 따라서 `setTimeout`으로 코드가 실행돼야 할 시간을 정해줘도 **정확하게 그 시간에 실행되지 않을수도 있다.**. + +첫 번째 인자로 넘긴 함수는 전역 객체가 실행시킨다. 따라서 인자로 넘겨진 함수 내부의 [`this`](#function.this)는 *전역* 객체를 가리키게 된다. + + function Foo() { + this.value = 42; + this.method = function() { + // this는 전역 객체를 가리키기 때문에 + console.log(this.value); // undefined를 출력한다. + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **Note:** `setTimeout`의 첫 번째 파라미터에 **함수** 객체를 넘겨야 하는 데 `setTimeout(foo(), 1000)`처럼 함수의 실행 결과를 넘기는 실수를 저지를 때가 잦다. 이럴 때 `setTimeout`은 그냥 `undefined`를 반환할 뿐이지 에러를 발생시키지 않는다. + +### 함수 호출을 쌓는(Stacking) `setInterval`함수. + +`setTimeout`은 딱 한 번 함수를 호출하지만 `setInterval`은 이름처럼 **지정한 시간마다** 함수를 실행시켜준다. 하지만 이 함수의 사용은 좀 생각해봐야한다. + +`setInterval`은 실행하는 코드가 일정시간 동안 블럭되도 계속해서 함수를 호출하기 때문에 주기가 짧은 경우 함수 호출이 쉽게 쌓여버린다. + + function foo(){ + // 1초 동안 블럭함. + } + setInterval(foo, 100); + +위 코드에서 `foo`함수는 호출될 때마다 1초씩 실행을 지연시킨다. + +하지만 `foo`함수가 블럭되더라도 `setInterval`함수는 계속해서 함수 호출을 쌓기 때문에 `foo`함수 호출이 끝나면 *10번* 이상의 함수 호출이 쌓여서 대기하고 있을수도 있다. +(역주: 따라서 함수 호출이 쌓이게 되면 원래 기대했던 실행 주기를 보장받지 못한다.) + +### 블럭되는 코드 해결법 + +앞에 문제를 해결하는 가장 쉽고 일반적인 방법은 `setTimeout` 함수에서 자기 자신을 다시 호출하는 방법이다. + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +이 방법은 함수 호출이 쌓이지도 않을 뿐만 아니라 `setTimeout` 호출을 해당 함수 안에서 관리하기 때문에 `foo` 함수에서 계속 실행할지 말지도 조절할 수 있다. + +### 타이머 없애기 + +`clearTimeout`과 `clearInterval` 함수로 setTimeout과 setInterval로 등록한 timeout과 interval을 삭제할 수 있다. `set` 함수들이 반환한 id를 저장했다가 `clear` 함수를 호출해서 삭제한다. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### 모든 타이머 없애기 + +등록한 timeout과 interval을 한꺼번에 제거하는 내장 함수는 없다. 따라서 좀 무식하지만 직접 구현해야 한다. + + // "모든" 타이머 지우기 + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +위와 같은 방법은 숫자가 미치지 못하는 타이머는 여전히 남아있을수 있다는 단점이 있다. 또 다른 해결 방법은 타이머가 반환하는 값이 항상 전보다 1만큼 큰 수를 반환한다는 점을 착안한 방법이다. + + // "모든" 타이머 지우기 + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +이 방법은 모든 주요 브라우저에서 문제없이 잘 동작하지만 ID가 항상 순차적이어야 한다고 표준에 명시된 것이 아니다. 그러므로 timeout ID를 모두 저장했다가 삭제하는 것이 가장 안전하다. 그러면 전부 깨끗하게 제거할 수 있다. + +### 보이지 않게 사용되는 `eval`함수 + +`setTimeout`과 `setInterval`의 첫 파라미터로 문자열을 넘길 수 있다. 하지만 내부적으로 `eval`을 사용하는 것이기 때문에 절대 사용해서는 안된다. + +> **Note:** timeout 함수는 ECMAScript 표준이 아니기 때문에 문자열로 넘어오는 첫번째 인자에 대한 해석은 구현체마다 다르다. 예를 들어, Microsoft의 JScript는 `eval`이 아니라 `Function` 생성자를 사용한다. + + function foo() { + // 이게 호출됨 + } + + function bar() { + function foo() { + // 이것은 절대 호출 안 됨 + } + setTimeout('foo()', 1000); + } + bar(); + +이 경우 `eval`이 [그냥(directly)](#core.eval) 호출되는 것이 아니다. `setTimeout`에 인자로 넘어간 문자열은 *전역* 스코프에서 실행되기 때문에 `bar`함수 영역에 있는 지역 변수 `foo`가 실행되는 것이 아니라 *전역* 스코프에 있는 `foo`가 실행된다. + +함수에 파라미터를 넘겨야 하면 스트링을 사용하지 말아야 한다. + + function foo(a, b, c) {} + + // 절대 사용하면 안 됨 + setTimeout('foo(1, 2, 3)', 1000) + + // 대신 익명 함수를 사용하는 게 좋다. + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Note:** `setTimeout(foo, 1000, 1, 2, 3)`처럼 사용하는 것도 가능하지만, 이것도 권장하지 않는다. [메소드](#function.this)를 사용할 때 잡아내기 어려운 에러가 날 수 있다. + +### 결론 + +`setTimeout`과 `setInterval`함수에 문자열 인자를 절대 사용해서는 안된다. 핸들러 함수에 인자를 넘기는 코드도 **절대** 좋은 코드가 아니다. *익명 함수*을 사용해서 호출해야 한다. + +그리고 `setInterval`은 해당 핸들러가 블럭되든 말든 상관하지 않기 때문에 되도록이면 쓰지말자. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/external/JavaScript-Garden/doc/ko/types/casting.md b/external/JavaScript-Garden/doc/ko/types/casting.md new file mode 100644 index 0000000..7a6e28e --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/types/casting.md @@ -0,0 +1,58 @@ +## 타입 캐스팅 + +JavaScript는 Weak Typing 언어이기 때문에 필요할 때마다 알아서 타입을 변환한다. + + // 다음은 모두 true + new Number(10) == 10; // Number.toString()이 호출되고 + // 다시 Number로 변환된다. + + 10 == '10'; // 스트링은 Number로 변환된다. + 10 == '+10 '; // 이상한 스트링 + 10 == '010'; // 엉뚱한 스트링 + isNaN(null) == false; // null은 NaN이 아녀서 0으로 변환된다. + + // 다음은 모두 false + 10 == 010; + 10 == '-10'; + +> **ES5 Note:** `0`으로 시작하는 숫자 리터럴은 8진수다. 하지만, ECMAScript 5의 strict 모드에서는 8진수로 더이상 해석하지 않는다. + +위와 같은 문제들은 ***반드시** [삼중 등호 연산자](#types.equality)를 이용해 해결하길 권한다. 물론 삼중 등호로 많은 결점을 보완할 수 있지만, 여전히 weak typing 시스템 때문에 생기는 많은 문제가 남아있다. + +### 기본 타입 생성자 + +`Number`나 `String` 같은 기본 타입들의 생성자는 `new` 키워드가 있을 때와 없을 때 다르게 동작한다. + + new Number(10) === 10; // False, Object와 Number + Number(10) === 10; // True, Number와 Number + new Number(10) + 0 === 10; // True, 타입을 자동으로 변환해주기 때문에 + +`new` 키워드와 함께 `Number` 같은 기본 타입의 생성자를 호출하면 객체를 생성하지만 `new` 없이 호출하면 형 변환만 시킨다. + +그리고 객체가 아니라 단순히 값이나 리터럴을 사용하면 타입 변환이 더 많이 일어난다. + +가능한 정확하게 타입을 변환해주는 것이 최선이다. + +### 스트링으로 변환하기 + + '' + 10 === '10'; // true + +숫자를 빈 스트링과 더하면 쉽게 스트링으로 변환할 수 있다. + +### 숫자로 변환하기 + + +'10' === 10; // true + +`+` 연산자만 앞에 붙여주면 스트링을 쉽게 숫자로 변환할 수 있다. + +### Boolean으로 변환하기 + +'!' 연산자를 두 번 사용하면 쉽게 Boolean으로 변환할 수 있다. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true diff --git a/external/JavaScript-Garden/doc/ko/types/equality.md b/external/JavaScript-Garden/doc/ko/types/equality.md new file mode 100644 index 0000000..a9daccb --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/types/equality.md @@ -0,0 +1,57 @@ +## 객체 비교하기 + +JavaScript에서 객체를 비교하는 방법은 두 가지가 있다. + +### 이중 등호 연산자 + +이중 등호 연산자는 `==`을 말한다. + +JavaScript는 Weak Typing을 따르기 때문에 이중 등호를 이용해 비교할 때 두 객체의 자료형을 **강제로** 변환한다. + + "" == "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 + +이 표는 이중 등호를 사용하면 왜 안되는지를 보여준다. 이 복잡한 변환 규칙은 실제로 골치 아픈 버그를 만들어 낸다. + +게다가 강제로 타입을 변환하게 되면 성능에도 영향을 준다. 예를 들어 문자와 숫자를 비교하려면 반드시 먼저 문자를 숫자로 변환해야 한다. + +### 삼중 등호 연산자 + +삼중 등호 연산자는 `===`을 말한다. + +삼중 등호는 강제로 타입을 변환하지 않는다는 사실을 제외하면 이중 등호와 동일하다. + + "" === "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 + +위 결과가 훨씬 더 명확하고 문제가 쉽게 드러난다. 삼중 등호를 사용하면 코드를 좀 더 튼튼하게 만들수 있고, 비교하는 두 객체의 타입이 다르면 더 좋은 성능을 얻을 수도 있다. + +### 객체 비교하기 + +이중 등호와(`==`)와 삼중 등호(`===`)는 둘 다 **값을 비교하는** 연산이지만 피연산자중에 Object 타입이 하나라도 있으면 다르게 동작한다. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +두 연산자 모두 두 객체의 값이 같은지를 비교하지 않고, 두 객체가 **같은 객체(identity)**인지를 비교한다. C에서 포인터를 비교하거나 Python의 is처럼 같은 인스턴스인지 비교하는 것이다. + +### 결론 + +**삼중 등호 연산자**를 사용할 것을 강력하게 권한다. 비교하기 위해서 타입 변환이 필요하면 언어의 복잡한 변환 규칙에 맡기지 말고 꼭 명시적으로 변환한 후에 비교해야 한다. diff --git a/external/JavaScript-Garden/doc/ko/types/instanceof.md b/external/JavaScript-Garden/doc/ko/types/instanceof.md new file mode 100644 index 0000000..621f812 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/types/instanceof.md @@ -0,0 +1,31 @@ +## `instanceof` 연산자 + +`instanceof`연산자는 두 피연산자의 생성자를 비교할때 사용하고 직접 만든 객체를 비교할 때 매우 유용하다. 내장 타입에 쓰는 경우에는 [typeof](#types.typeof)처럼 거의 쓸모가 없다. + +### 커스텀 객체를 `intanceof`로 비교하기 + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Bar.prototype에 함수 객체인 Foo를 할당하면 + // Bar의 인스턴스는 Foo의 인스턴스가 아니다. + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### 기본 내장 객체 타입을 `intanceof`로 비교하기 + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +JavaScript 컨텍스트마다(웹 브라우저의 도큐먼트 같은) 객체의 생성자는 다를 수밖에 없어서 `instanceof`는 다른 JavaScript 컨텍스트에 있는(웹 브라우저의 다른 도큐먼트에 있는) 객체와는 비교할 수 없다. + +### 결론 + +`instanceof`는 한 JavaScript 컨텍스트 내에서 사용자가 만든 타입의 객체를 비교할 때에만 유용하다. [`typeof`](#types.typeof)처럼 다른 목적으로는 사용하지 않는 것이 좋다. diff --git a/external/JavaScript-Garden/doc/ko/types/typeof.md b/external/JavaScript-Garden/doc/ko/types/typeof.md new file mode 100644 index 0000000..a2db959 --- /dev/null +++ b/external/JavaScript-Garden/doc/ko/types/typeof.md @@ -0,0 +1,63 @@ +## `typeof` 연산자 + +`typeof` 연산자도 [`instanceof`](#types.instanceof) 연산자와 함께 JavaScript에서 치명적으로 잘못 설계된 부분이다. 이건 정말이지 아무짝에도 쓸모가 없다. + +`instanceof` 연산자는 그래도 여전히 쓸만한 데가 좀 있는데 `typeof` 연산자는 객체의 타입을 검사하는 것 외에는 쓸만한데가 없고, 이마저도 거의 쓸일이 없다. + +> **Note:** `typeof` 연산자는 함수처럼 `typeof(obj)`로 사용할수 있지만 함수 호출은 아니다. 괄호 안의 값은 `typeof`의 피연산자로 적용되고 그 결과가 반환된다. `typeof`라는 함수는 **없다**. + +### JavaScript 타입 표 + + 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 + +위 표에서 *Type*은 `typeof`가 반환하는 값이다. 위 표에서처럼 일치되는 값이 거의 없다. + +위 표에서 *Class*는 객체 내부에 있는 `[[Class]]` 프로퍼티의 값을 말한다. + +> **표준**에는 `[[Class]]`의 값은 `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`중 하나라고 나와있다. + +`[[Class]]` 프로퍼티의 값을 가져다 쓰려면 `Object.prototype`의 `toString` 메소드를 사용한다. + +### 객체의 클래스 + +표준에 의하면 `[[Class]]` 값을 얻는 방법은 `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 + +`Object.prototype.toString`은 [this](#function.this)의 `[[Class]]` 값을 가져오는 것이니까 this를 obj로 바꾸어 사용한다. + +> **ES5 Note:** ECMAScript 5에서 `Object.prototype.toString`의 컨텍스트가 `null`과 `undefined`일 때 `Object`가 아니라 각각 `Null`과 `Undefined`를 반환하도록 수정됐다. + +### 변수가 Undefined인지 확인하기 + + typeof foo !== 'undefined' + +위 코드는 `foo`가 정의됐는지 아닌지를 확인해준다. 정의되지 않은 변수에 접근하면 `ReferenceError` 나는데 이것을 방지할 수 있다. `typeof`가 유용한 건 이때뿐이다. + +### 결론 + +객체의 타입을 검사하려면 `Object.prototype.toString`를 사용해야 한다. 다른 방법은 신뢰할 수 없다. 위 표에서 보여준 것처럼 typeof가 반환하는 값은 표준에 나와 있지 않기 때문에 구현방법도 제각각이다. + +변수가 정의됐는지 확인할 때를 제외하고 **가급적** `typeof`는 피해야한다. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/language.json b/external/JavaScript-Garden/doc/language.json new file mode 100644 index 0000000..df12ab7 --- /dev/null +++ b/external/JavaScript-Garden/doc/language.json @@ -0,0 +1,4 @@ +{ + "default": "en", + "listed": ["by","en","es","fi","fr","hu","it","ja","ko","pl","ptbr","ru","tr","zh","zhtw"] +} diff --git a/external/JavaScript-Garden/doc/pl/array/constructor.md b/external/JavaScript-Garden/doc/pl/array/constructor.md new file mode 100644 index 0000000..e715c84 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/array/constructor.md @@ -0,0 +1,37 @@ +## 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 właściwością `length` ustawioną 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. + diff --git a/external/JavaScript-Garden/doc/pl/array/general.md b/external/JavaScript-Garden/doc/pl/array/general.md new file mode 100644 index 0000000..499e0d6 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/array/general.md @@ -0,0 +1,58 @@ +## 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`](#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, jedynym sposobem, aby wykluczyć te właściwości, jest użycie +[`hasOwnProperty`](#object.hasownproperty). Wówczas pętla staje się jednak +**dwadzieścia 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 wyszukiwania tej właściwości przy każdej iteracji w pętli. +Najnowsze silniki JavaScript **mogą** zastosować w tym +przypadku optymalizację. Nie ma jednak możliwości 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ść. + diff --git a/external/JavaScript-Garden/doc/pl/core/eval.md b/external/JavaScript-Garden/doc/pl/core/eval.md new file mode 100644 index 0000000..9a0ae0d --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/core/eval.md @@ -0,0 +1,48 @@ +## 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 się `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](#other.timeouts) `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`. + diff --git a/external/JavaScript-Garden/doc/pl/core/semicolon.md b/external/JavaScript-Garden/doc/pl/core/semicolon.md new file mode 100644 index 0000000..7f1410c --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/core/semicolon.md @@ -0,0 +1,117 @@ +## 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 + +> **Uwaga:** Parser JavaScript nie potrafił "odpowiednio" zinterpretować +> 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 +**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. + diff --git a/external/JavaScript-Garden/doc/pl/core/undefined.md b/external/JavaScript-Garden/doc/pl/core/undefined.md new file mode 100644 index 0000000..6fb9ac9 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/core/undefined.md @@ -0,0 +1,69 @@ +## `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. + +> Uwaga ES55: `undefined` w ECMAScript 5 **nie będzie już** *nadpisywalna* w trybie +> 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 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](#function.scopes), 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`. + diff --git a/external/JavaScript-Garden/doc/pl/function/arguments.md b/external/JavaScript-Garden/doc/pl/function/arguments.md new file mode 100644 index 0000000..098c2de --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/arguments.md @@ -0,0 +1,121 @@ +## 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. + +> **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 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`. + +> **Uwaga ES5:** *gettery* and *settery* nie są tworzone w trybie strict mode + +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][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 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. + +> **Uwaga ES5:** W trybie strict mode, `arguments.callee` wyrzuci `TypeError` +> ponieważ korzystanie z niej jest przestarzałe. + +[1]: http://en.wikipedia.org/wiki/Inlining + diff --git a/external/JavaScript-Garden/doc/pl/function/closures.md b/external/JavaScript-Garden/doc/pl/function/closures.md new file mode 100644 index 0000000..df78210 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/closures.md @@ -0,0 +1,101 @@ +## 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 +[funkcję](#function.scopes), 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ęstszych 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](#function.scopes). + + 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) + } + diff --git a/external/JavaScript-Garden/doc/pl/function/constructors.md b/external/JavaScript-Garden/doc/pl/function/constructors.md new file mode 100644 index 0000000..d9be52a --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/constructors.md @@ -0,0 +1,131 @@ +## 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`](#object.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łana 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`](#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ść. + + 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](#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`). +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](#function.closures), to posiada +pewne wady. + + 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. + diff --git a/external/JavaScript-Garden/doc/pl/function/general.md b/external/JavaScript-Garden/doc/pl/function/general.md new file mode 100644 index 0000000..f9c3247 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/general.md @@ -0,0 +1,53 @@ +## Deklaracje funkcji i wyrażenia funkcyjne + +Funkcje 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. + +### Deklaracja funkcji + + function foo() {} + +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. + + 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](#core.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](#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 diff --git a/external/JavaScript-Garden/doc/pl/function/scopes.md b/external/JavaScript-Garden/doc/pl/function/scopes.md new file mode 100644 index 0000000..bb43387 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/scopes.md @@ -0,0 +1,235 @@ +## 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 + } + +> **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), +> 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. + +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, 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*. Brak użycia `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, +w przypadku kodu, który posiada wielu tysięcy linii, brak `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 modyfikacja +zewnętrznego zasięgu. + +### Lokalne zmienne + +Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry [funkcji](#function.general) +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 zasięgu 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). + +Analizując poniższy kod bez wiedzy na temat hoistingu możemy odnieść wrażenie, +że zobaczymy błąd `ReferenceError`. + + // sprawdź, 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 + + // upewnij się, ż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`](#function.this), która wskazuje +na *aktualny obiekt*. + +Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę +[`arguments`](#function.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 funkcja została nazwana `foo`, skorzystaj z tego. + 4. Przejdź do zewnętrznego zasięgu i przejdź do kroku **#1**. + +> **Uwaga:** Jeżeli jeden z parametrów fukcji został nazwany `arguments`, +> nie zostanie utworzony domyślny obiekt `arguments`. + +### 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* (inaczej: Immediately-Invoked +Function Expression - IIFE). + + (function() { + // autonomiczna "przestrzeń nazw" + + window.foo = function() { + // wyeksponowane domkniecie (closure) + }; + + })(); // natychmiastowe wykonanie funkcji + +Anonimowe funkcje są rozpoznane jako [wyrażenia](#function.general), 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. + diff --git a/external/JavaScript-Garden/doc/pl/function/this.md b/external/JavaScript-Garden/doc/pl/function/this.md new file mode 100644 index 0000000..adf7e1d --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/function/this.md @@ -0,0 +1,111 @@ +## 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. + +### 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 wskazywało na obiekt *global* + +> **Uwaga ES5:** W trybie strict mode, przypadki z globalnym zasięgiem nie mają miejsca. +> W tym przypadku `this` zwróci `undefined` zamiast wartości. + +### 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](#function.constructors). 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`. + +> **Uwaga:** `this` **nie może** zostać użyte jako referencja do obiektu wewnątrz literału +> `Object`. Zatem `var obj = {me: this}` **nie** spowoduje, że `me` będzie wskazywać na `obj`, +> `this` zostaje związane z wartością tylko w powyższych pięciu wylistowanych przypadkach. + +### Częste pułapki + +Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być +traktowany 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)](#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 +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](#object.prototype) 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ę. + diff --git a/external/JavaScript-Garden/doc/pl/index.json b/external/JavaScript-Garden/doc/pl/index.json new file mode 100644 index 0000000..16b65ff --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/index.json @@ -0,0 +1,74 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden - ogród JavaScript po polsku", + "description": "Przewodnik po dziwactwach i wadach języka JavaScript.", + "sections": [ + { + "title": "Wstęp", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "translators", + "hosting", + "license" + ] + }, + { + "title": "Obiekty", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Funkcje", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Tablice", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Typy", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Jądro", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Inne", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/pl/intro/authors.md b/external/JavaScript-Garden/doc/pl/intro/authors.md new file mode 100644 index 0000000..380c158 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/authors.md @@ -0,0 +1,8 @@ +## Autorzy + +Ten przewodnik jest dziełem dwóch uroczych użytkowników [Stack Overflow][1], +[Ivo Wetzel][2] (Treść) oraz [Zhang Yi Jiang][3] (Projekt). + +[1]: http://stackoverflow.com/ +[2]: http://stackoverflow.com/users/170224/ivo-wetzel +[3]: http://stackoverflow.com/users/313758/yi-jiang \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/pl/intro/contributors.md b/external/JavaScript-Garden/doc/pl/intro/contributors.md new file mode 100644 index 0000000..64ae249 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/contributors.md @@ -0,0 +1,4 @@ +## Współtwórcy + +- [Współtwórcy](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + diff --git a/external/JavaScript-Garden/doc/pl/intro/hosting.md b/external/JavaScript-Garden/doc/pl/intro/hosting.md new file mode 100644 index 0000000..19ac902 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/hosting.md @@ -0,0 +1,6 @@ +## Hosting +JavaScript Garden znajduje się na serwerach GitHub, ale dzięki wsparciu +[Cramer Development] [1] posiadamy również mirror na serwerze [JavaScriptGarden.info] [2]. + +[1]: http://cramerdev.com/ +[2]: http://javascriptgarden.info/ diff --git a/external/JavaScript-Garden/doc/pl/intro/index.md b/external/JavaScript-Garden/doc/pl/intro/index.md new file mode 100644 index 0000000..03ea149 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/index.md @@ -0,0 +1,13 @@ +## 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][1] na stronach Mozilla Developer Network. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide diff --git a/external/JavaScript-Garden/doc/pl/intro/license.md b/external/JavaScript-Garden/doc/pl/intro/license.md new file mode 100644 index 0000000..5b0e855 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/license.md @@ -0,0 +1,11 @@ +## Licencja + +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ówki, zgłoś proszę +[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 +[2]: https://github.com/BonsaiDen/JavaScript-Garden +[3]: https://github.com/BonsaiDen/JavaScript-Garden/issues +[4]: http://chat.stackoverflow.com/rooms/17/javascript diff --git a/external/JavaScript-Garden/doc/pl/intro/translators.md b/external/JavaScript-Garden/doc/pl/intro/translators.md new file mode 100644 index 0000000..5197a9f --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/intro/translators.md @@ -0,0 +1,7 @@ +## Tłumaczenie + + - [Łukasz Kufel][1] + - [Maciej Ciemborowicz][2] + +[1]: http://qfel13.pl +[2]: http://blog.ciemborowicz.pl diff --git a/external/JavaScript-Garden/doc/pl/object/forinloop.md b/external/JavaScript-Garden/doc/pl/object/forinloop.md new file mode 100644 index 0000000..9a6654a --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/object/forinloop.md @@ -0,0 +1,50 @@ +## 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). + + // 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ż 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, +> będzie się ona stawała coraz wolniejsza przy dodaniu każdej kolejnej warstwy +> dziedziczenia do obiektu. + +### Filtrowania przy użyciu `hasOwnProperty` + + // foo z przykładu powyżej + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +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 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 ani tego, czy +natywne prototypy zostały rozszerzone, czy nie. + +[1]: http://www.prototypejs.org/ diff --git a/external/JavaScript-Garden/doc/pl/object/general.md b/external/JavaScript-Garden/doc/pl/object/general.md new file mode 100644 index 0000000..b3b05e5 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/object/general.md @@ -0,0 +1,102 @@ +## Wykorzystanie obiektów i ich właściwości + +Wszystko w JavaScripcie zachowuje się jak obiekt, z dwoma wyjątkami +[`null`](#core.undefined) oraz [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +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 + +Istnieje kilka rozwiązań, dzięki 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 najpierw zewaluowane + +### Obiekty jako typy danych + +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ść stworzenia obiektu prostego. +Ten nowy obiekt będzie [dziedziczył](#object.prototype) z `Object.prototype` oraz +nie będzie posiadał żadnych [własnych właściwości](#object.hasownproperty). + + 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 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 wyrzucenia +błędu podczas odczytu nieistniejącej właściwości. + +### Usuwanie właściwości + +Jedynym sposobem na faktyczne 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': 'jestem słowem kluczowym, więc muszę być w cudzysłowie', + delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError + }; + +Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów +lub apostrofów) lub jako string (w cudzysłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript, +powyższy kod wyrzuci błąd `SyntaxError` dla implementacji JavaScript poniżej 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. + +[1]: http://pl.wikipedia.org/wiki/Tablica_asocjacyjna + diff --git a/external/JavaScript-Garden/doc/pl/object/hasownproperty.md b/external/JavaScript-Garden/doc/pl/object/hasownproperty.md new file mode 100644 index 0000000..59ae744 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/object/hasownproperty.md @@ -0,0 +1,53 @@ +## `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órej wszystkie obiekty dziedziczą z `Object.prototype`. + +> **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 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 + 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 będzie 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 kontekstem + // `this` ustawionym na foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### Wnioski + +**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/external/JavaScript-Garden/doc/pl/object/prototype.md b/external/JavaScript-Garden/doc/pl/object/prototype.md new file mode 100644 index 0000000..3cabc20 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/object/prototype.md @@ -0,0 +1,118 @@ +## 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 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óry 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 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 większości przypadków, +> niepożądanym efektem. + + 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', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +W powyższym przykładzie obiekt `test` będzie dziedziczył z obydwu, tj. +`Bar.prototype` i `Foo.prototype`, 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 jedyną instancją `Foo` i stała się jego prototypem. +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` tylko obiektu +> funckji `Foo`. Zatem łańcuch prototypów nie będzie zawierał `Foo.prototype`, +> 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 bez nazwy. + +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). + +### 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 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. + +### Rozszerzanie natywnych prototypów + +Rozszerzanie `Object.prototype` lub innego prototypu wbudowanych typów jest jednym z +najczęściej nadużywanej częsci języka JavaScript. + +Technika ta nazywana jest [monkey patching][1] i łamie zasady *enkapsulacji*. +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 się w nowszych silnikach JavaScript, np. [`Array.forEach`][3] + +### Wnioski + +Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczenia, +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. + +[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/external/JavaScript-Garden/doc/pl/other/timeouts.md b/external/JavaScript-Garden/doc/pl/other/timeouts.md new file mode 100644 index 0000000..97a4774 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/other/timeouts.md @@ -0,0 +1,158 @@ +### `setTimeout` i `setInterval` + +Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania +funkcji przy użyciu funkcji `setTimeout` i `setInterval`. + +> **Note:** Funkcje czasowe nie są częścią standardu ECMAScript. Jest to część +> standardu [DOM][1]. + + function foo() {} + 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**. + +**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 +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 wskazywać 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(); + +> **Uwaga:** Ponieważ `setTimeout` przyjmuje **obiekt funkcji** jako pierwszy +> 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 +> błędu. + +### 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 kilkukrotnie. Może się to zdarzyć +szczególnie 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, +w kolejce do wywołania będzie już czekało kolejne **dziesięć** wywołań tej funkcji. + +### Radzenie sobie z możliwymi blokadami + +Najprostszą, 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 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 + +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 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 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` + +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`. + +> **Uwaga:** Ponieważ funkcje budzików **nie** są częścią specyfikacji standardu +> ECMAScript, działanie tych funkcji nie jest określone w momencie, gdy zostanie +> do nich przekazany string. Na przykład Microsoftowy JScript wykorzystuje +> konstruktor `Function` zamiast funkcji `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 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 **niestosowanie** 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(1, 2, 3); + }, 1000) + +>**Uwaga:** Mimo że możliwe jest wykorzystanie składni +> `setTimeout(foo, 1000, 1, 2, 3)`, nie zaleca się korzystania z niej, ponieważ +> może to prowadzić do subtelnych błędów podczas wykorzystania [metod](#function.this). + +### Wnioski + +**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ć korzystania z `setInterval`, ponieważ planista może +zablokować wykonanie JavaScriptu. + +[1]: http://pl.wikipedia.org/wiki/Obiektowy_model_dokumentu "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/pl/types/casting.md b/external/JavaScript-Garden/doc/pl/types/casting.md new file mode 100644 index 0000000..099ac0c --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/types/casting.md @@ -0,0 +1,72 @@ +## 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. + + // 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 == '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'; + +> **Uwaga ES5: Literały liczbowe zaczynające się 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** 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 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 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. + +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 + + diff --git a/external/JavaScript-Garden/doc/pl/types/equality.md b/external/JavaScript-Garden/doc/pl/types/equality.md new file mode 100644 index 0000000..c655a79 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/types/equality.md @@ -0,0 +1,71 @@ +## 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łędów. + +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żsamość** 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](#types.casting), a nie pozostawiona trudnym regułom koercji +obowiązującym w języku. + diff --git a/external/JavaScript-Garden/doc/pl/types/instanceof.md b/external/JavaScript-Garden/doc/pl/types/instanceof.md new file mode 100644 index 0000000..aee5fe8 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/types/instanceof.md @@ -0,0 +1,41 @@ +## 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](#types.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ć 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/external/JavaScript-Garden/doc/pl/types/typeof.md b/external/JavaScript-Garden/doc/pl/types/typeof.md new file mode 100644 index 0000000..666d9b5 --- /dev/null +++ b/external/JavaScript-Garden/doc/pl/types/typeof.md @@ -0,0 +1,87 @@ +## Operator `typeof` + +Operator `typeof` (razem z operatorem [`instanceof`](#types.instanceof)) jest +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, +natomiast `typeof` ma tylko jeden praktyczny przypadek użycia, który na dodatek +**nie** jest związany z sprawdzaniem typu obiektu. + +> **Uwaga:** Do wywołania operatora `typeof` może zostać użyta składnia funkcyjna np. +> `typeof(obj)`, ale nie jest to wywołanie funkcji. Dwa nawiasy zwrócą obiekt +> znajdujący się wewnątrz i zwrócona wartość stanie się operandem operatora +> `typeof`. **Nie istnieje** funkcja `typeof`. + +### 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 się do wartości wewnętrznej właściwości `[[Class]]` obiektu. + +> **Fragment Specyfikacji:** Wartość `[[Class]]` może być jednym z poniższych +> stringów. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +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](#function.this) ustawioną na obiekt, dla której wartość właściwości +`[[Class]]` ma zostać odczytana. + +> **Uwaga ES5:** Dla zwiększenia wygody wartość zwracana przez +> `Object.prototype.toString` dla `null` i `undefined` została zmieniona +> z `Object` na `Null` i `Undefined` w ECMAScript 5. + +### 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 **jeśli to tylko możliwe**. + diff --git a/external/JavaScript-Garden/doc/ptbr/array/constructor.md b/external/JavaScript-Garden/doc/ptbr/array/constructor.md new file mode 100644 index 0000000..eaaf1c2 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/array/constructor.md @@ -0,0 +1,29 @@ +## O construtor `Array` + +Uma vez que o construtor `Array` é ambíguo na forma como ele lida com seus parâmetros, +o uso da notação `[]` é fortemente recomendado ao criar novo arrays. + + [1, 2, 3]; // Resultado: [1, 2, 3] + new Array(1, 2, 3); // Resultado: [1, 2, 3] + + [3]; // Resultado: [3] + new Array(3); // Resultado: [] + new Array('3') // Resultado: ['3'] + +Nos casos onde somente um argumento é passado para o construtor `Array` e quando o argumento é +um `Number`, o construtor retornará um novo array *sem elementos* com a propriedade `length` configurada de acordo com o valor do argumento. +É importante perceber que **somente** a propriedade `length` do novo array será configurada desta maneira; os índices do array não serão inicializados. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, o índice não foi definida + +Ser capaz de definir o comprimento de um array antecipadamente é útil em poucos casos, +como ao replicar uma string, em que se evita o uso de um loop. + + new Array(count + 1).join(stringToRepeat); + +### Conclusão + +O uso de literais é preferencial na inicialição de Arrays. São curtos, possuem uma sintaxe limpa, e contribuem para a legibilidade do código. + diff --git a/external/JavaScript-Garden/doc/ptbr/array/general.md b/external/JavaScript-Garden/doc/ptbr/array/general.md new file mode 100644 index 0000000..10543a8 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/array/general.md @@ -0,0 +1,59 @@ +## Iteração com Arrays e propriedades + +Embora arrays em JavaScript sejam objetos, não existem boas razões para utilizar +o [`for in`](#object.forinloop) loop. De fato, existem muitas boas razões para +**evitar** o uso de `for in` com arrays. + +> **Nota:** JavaScript arrays **não** são *arrays associativos*. JavaScript utiliza +> [objects](#object.general) apenas para mapear chaves com valores. Enquanto arrays associativos +> **preservam** a ordem, objetos **não preservam**. + +Uma vez que o `for in` loop enumera todas as propriedades que estão na cadeia +prototype e visto que o único modo de excluir tais propriedades é por meio do uso +do [`hasOwnProperty`](#object.hasownproperty), ele chega a ser **vinte vezes** +mais custoso que o uso normal do `for` loop. + +### Iteração + +A fim de atingir a melhor performance ao interagir sobre arrays, +a melhor opção é utilizar o clássico `for` loop. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Existe um detalhe importante no exemplo acima , que é o caching +do comprimento do array via `l = list.length`. + +Embora a propriedade `length` esteja definida no próprio array, ainda existe +um trabalho extra ao executar a busca em cada iteração do array. +Enquanto que recentes engines JavaScript **talvez** implementem um otimização +para este caso, não existe uma maneira de saber quando o código será executado em uma +dessas novas engines. + +De fato, deixando de lado o armazenamento em caching pode resultar em um loop **duas vezes mais rápido** +do que com o armazenamento em caching. + +### A propriedade `length` + +Enquanto que o *getter* da propriedade `length` retorna o total de elementos +que estão contidos no array, o *setter* pode ser usado para **truncar** o array. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo.push(4); + foo; // [1, 2, 3, undefined, undefined, undefined, 4] + +Atribuir um valor de menor para length trunca o array. Por outro lado, incrementando +o valor de length cria um array esparso. + +### Conclusão + +Para melhor performance, é recomendado o uso do `for` loop e o cache da propriedade +`length`. O uso do `for in` loop na iteração com array é um sinal de código mal escrito +e tendencioso a apresentar defeitos, além de ter performance ruim. + diff --git a/external/JavaScript-Garden/doc/ptbr/core/delete.md b/external/JavaScript-Garden/doc/ptbr/core/delete.md new file mode 100644 index 0000000..949c24d --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/core/delete.md @@ -0,0 +1,85 @@ +## O operador `delete` + +Em resumo, é *impossível* remover variáveis globais, funções e outras coisas em JavaScript +que tenham o atributo `DontDelete` definido. + +### Código global e código de função + +Quando uma variável ou função é definida no escopo global ou em +um [escopo de função](#function.scopes) ela passa a ser uma propriedade de ambos +objeto Activation e do objeto Global. Tais propriedades possuem um conjunto de atributos, um dos quais é o `DontDelete`. +Declarações de funções e variáveis em código global e em código de função +sempre criam propriedades com `DontDelete`, e portanto não podem ser removidas. + + // variável global: + var a = 1; // DontDelete está definido + delete a; // false + a; // 1 + + // função comum: + function f() {} // DontDelete está definido + delete f; // false + typeof f; // "function" + + // mudar o valor do atributo não ajuda: + f = 1; + delete f; // false + f; // 1 + +### Propriedades explícitas + +Propriedades definidas explicitamente podem ser apagadas normalmente. + + // definição explícita de propriedade: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +No exemplo acima, `obj.x` e `obj.y` podem ser removidos por que eles não possuem o +atributo `DontDelete`. Este é o motivo pelo qual o exemplo abaixo também funciona. + + // Desconsiderando o IE, isto funciona bem: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - apenas uma variável global + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Aqui nós utilizamos um truque para remover `a`. Aqui o [`this`](#function.this) +faz referência ao objeto Global e declara explicitamente a variável `a` como +sua propriedade a qual nos permite removê-la. + +O IE (pelo menos 6-8) possui defeitos, então o código acima não funciona. + +### Argumentos de função e propriedades nativas + +Argumentos de função, [objetos `arguments`](#function.arguments) e +propriedades nativas tambêm possuem o `DontDelete` definido. + + // argumentos de funções e propriedades: + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### Objetos hosts + +O comportamento do operador `delete` pode ser imprevisível para objetos hosts. +Devido a especificação, objetos hosts têm permissão para implementar qualquer tipo de comportamento. + +### Conclusão + +O operador `delete` freqüentemente apresenta um comportamento inesperado e só +pode ser usado com segurança para remover propriedades definidas explicitamente em objetos normais. diff --git a/external/JavaScript-Garden/doc/ptbr/core/eval.md b/external/JavaScript-Garden/doc/ptbr/core/eval.md new file mode 100644 index 0000000..e7608cd --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/core/eval.md @@ -0,0 +1,45 @@ +## Por que não utilizar `eval` + +A função `eval` executará uma string de código JavaScript no escopo local. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Entretanto, `eval` somente é executado no escopo local quando é chamado diretamente +*e* quando o nome da função chamada é `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +O uso de `eval` deve ser evitado. 99.9% de seu "uso" pode ser alcançado **sem** ele. + +### `eval` dissimulado + +As [funções timeout](#other.timeouts) `setTimeout` e `setInterval` podem ambas receberem uma string +como primeiro argumento. Tais strings **sempre** serão executadas no escopo global uma vez que +`eval` não é chamado diretamente, naquele caso. + +### Problemas de segurança + +`eval` também é considerado um problema de segurança, por que executa **qualquer** código dado. +Ele **nunca** deve ser utilizado com strings de origens duvidosas ou desconhecidas. + +### Conclusão + +`eval` nunca deve ser utilizado. Qualquer código que faça uso de `eval` seve ser questionado +em sua utilidade, performance e segurança. Se algo necessita de `eval` para funcionar, então **não** deve ser utilizado. +Um *design melhor* deve ser utilizado, um que não faça uso de `eval`. + diff --git a/external/JavaScript-Garden/doc/ptbr/core/semicolon.md b/external/JavaScript-Garden/doc/ptbr/core/semicolon.md new file mode 100644 index 0000000..843d7db --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/core/semicolon.md @@ -0,0 +1,100 @@ +## Inserção automática do ponto e vírgula + +Apesar do JavaScript possuir uma sintaxe no estilo C, o uso do ponto e vírgula **não** é obrigatório. + +JavaScript não é uma linguagem 'semicolon-less'. De fato, o ponto e vírgula é necessário para o interpretação do código. Entretanto, o parser do JavaScript insere o **ponto e vírgula** automaticamente sempre que ocorrer um error de parser, decorrente da falta do ponto e vírgula. + + var foo = function() { + } // parse error, semicolon expected + test() + +A inserção acontece e o parser realiza uma nova tentativa. + + var foo = function() { + }; // no error, parser continues + test() + +A inseção automática de ponto e vírgula é considerada um dos **maiores** equívocos no design da linguagem pois pode influenciar no comportamento do código. + +### Como funciona + +O código abaixo não possui ponto e vírgula, então fica à cargo do parser inserir o ponto e vírgula onde julgar necessário. + + (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) + +Abaixo está o resultado do processamento do parser. + + (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 + +> **Nota:** O parser do JavaScript não manipula corretamente 'return statements' que são seguidos de uma nova linha. Apesar de não ser necessariamente uma > > > falha da inserção automática do ponto e vírgula, ainda pode gerar efeitos colaterais não-esperados. + +O parser mudou o comportamento do código acima drásticamente. Em determinados casos, o parser **não procede** como o esperado. + +### Parênteses + +No caso de parênteses, o parser **não** insere o ponto e vírgula. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Este código é interpretado em uma só linha. + + log('testing!')(options.list || []).forEach(function(i) {}) + +As chances de `log` não retornar uma função são **muito** altas; portanto, o código acima irá produzir um `TypeError` informando que `undefined is not a function`. + +### Conclusão + +É **fortemente** recomendado que nunca se omita o ponto e vírgula. Também é recomendado que chaves sejam mantidas na mesma linha que seus statements e que nunca sejam omitadas em declações de uma só linha como `if` / `else` statements. Tais medidas não somente melhorarão a consistência do código, como também irão previnir alteração no comportamento do código por má interpretação do parser do JavaScript. + diff --git a/external/JavaScript-Garden/doc/ptbr/core/undefined.md b/external/JavaScript-Garden/doc/ptbr/core/undefined.md new file mode 100644 index 0000000..cbaedcc --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/core/undefined.md @@ -0,0 +1,68 @@ +## `undefined` e `null` + +JavaScript tem duas formas distintas para representar o nada, `null` e `undefined`, com +o último sendo o mais útil. + +### O valor `undefined` + +`undefined` é um tipo com um valor exato : `undefined`. + +A linguagem também define uma variável global que tem como valor `undefined`; +esta variável também é chamada `undefined`. Entretanto tal variável *não é nem* uma constante +*muito menos* uma palavra-chave da linguagem. Isto significa que seu *valor* pode ser facilmente +sobrescrito. + +> **Nota ES5:** `undefined` em ECMAScript 5 **não dispõe** mais de permissão para escrita no modo estrito, porém +> sua denominação ainda pode ser confundida com, por exemplo, uma função com o nome `undefined`. + +Aqui estão alguns exemplos de quando o valor `undefined` é retornado: + + - Acessando uma variável global (não-modificada) `undefined`. + - Acessando uma variável declarada *mas não* ainda inicializada. + - Retornos implícitos de funções devido ao esquecimento do `return` statement. + - `return` statements que explicimente retornam o nada. + - Busca por propriedades não-existentes. + - Parâmetros de funções que não têm um valor explícito definido. + - Qualquer coisa que tenha sido definida como `undefined`. + - Qualquer expressão no formato `void(expressão)` + +### Manipulando mudanças no valor de `undefined` + +Uma vez que a variável global `undefined` apenas mantém uma cópia do valor *atual* de `undefined`, atribuir-lhe +um novo valor **não** muda o valor do tipo `undefined`. + +Ainda, a fim de comparar alguma coisa com o valor de `undefined`, é necessário que +primeiro se retorne o `undefined`. + +A fim de proteger o código contra uma possível sobrescrtia da variável `undefined`, uma +técnica comum utilizada é a de adicionar um parâmetro adicional em um [wrapper anônimo](#function.scopes) + que não recebe argumentos. + + var undefined = 123; + (function(something, foo, undefined) { + // undefined no escopo local agora + // refer-se ao valor `undefined` + + })('Hello World', 42); + +Outra maneira de atingir o mesmo efeito seria utilizar uma declaração dentro do wrapper. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +A única diferença aqui é a que a última versão resulta na redução de 4 bytes, e não existe +outro `var` statement dentro do wrapper anônimo. + +### Usos do `null` + +Enquanto que `undefined` no contexto da linguagem JavaScript é normalmente utilizado +como um *null*, o atual `null` (ambos o tipo e o literal) é mais ou menos um outro tipo de dado. + +Ele é utilizado internamente pelo JavaScript (como na declaração no fim da cadeia prototype +ao definir `Foo.prototype = null`), porém na maioria dos casos, pode ser substituido por `undefined`. + + diff --git a/external/JavaScript-Garden/doc/ptbr/function/arguments.md b/external/JavaScript-Garden/doc/ptbr/function/arguments.md new file mode 100644 index 0000000..33c3214 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/arguments.md @@ -0,0 +1,110 @@ +## O objeto `arguments` + +Todo escopo de uma função em JavaScript tem acesso à variável especial `arguments`. +Esta variável armazena uma lista de todos os argumentos que foram passados para a função. + +> **Nota:** No caso em que `arguments` tenha sido definido dentro do escopo da função por meio +> de `var` statement ou que este seja o nome de um parâmetro formal, o objeto `arguments` não será criado. + +O objeto `arguments` **não** é um `Array`. Enquanto que ele possui uma semântica +parecida com a de um array - a saber a propriedade `length` - ele não herda de `Array.prototype` +e é de fato um `Object`. + +Devido a isto, **não** é possível usar os métodos padrões de array como `push`, +`pop` ou `slice` no `arguments`. Enquanto que a iteração com um simples `for` loop funciona bem, +é necessário convertê-lo para um `Array` a fim de usar os métodos padrões de `Array`. + +### Convertendo em um Array + +O código abaixo irá retornar um novo `Array` contendo todos os elementos do +objeto `arguments`. + + Array.prototype.slice.call(arguments); + +Por este tipo de conversão ser **lenta**, seu uso em porções de código que apresentam performance crítica **não é recomendado**. + +### Passando argumentos + +O código abaixo é a maneira recomendada de se passar argumentos de uma função para outra. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Outro truque é o de usar ambos `call` e `apply` juntos para criar 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); + }; + + +### Parâmetros formais Formal Parameters and Arguments Indices + +O objeto `arguments` cria funções *getter* e *setter* para suas propriedades, +bem como os parâmetros formais da função. + +Como resultado, alterando o valor de um parâmetro formal também mudará o valor +da propriedade correspondente no objeto `arguments`, e vice versa. + + 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); + +### Mitos e verdades sobre performance + +A única vez em que o objeto `arguments` não é criado é quando é declarado como um nome dentro de uma função +ou declarado como um de seus parâmetros formais. Não importa se ele é usado ou não. + +Ambos *getters* e *setters* são *sempre* criados; desta maneira, usá-los não causa impacto +de performance, especialmente não em código do mundo real, onde existe mais de um simples +acesso às propriedades do objeto `arguments`. + +> **Nota ES5:** Estes *getters* e *setters* não são criados no strict mode. + +Entretando, existe um caso em que a performance é drasticamente reduzida +em engines modernas de JavaScript. Este caso é o uso de `arguments.callee` + + function foo() { + arguments.callee; // Faça alguma coisa com os objeto deta função + arguments.callee.caller; // e o calling do objeto da função + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Would normally be inlined... + } + } + +Isto não somente acaba com possíveis ganhos de performance que resultariam de inlining, +mas também quebram o encapsulamento pois a função agora depende de uma chamada específica de contexto. + +O uso de `arguments.callee` é **fortemente desencorajado**. + +> **Nota ES5:** No strict mode, `arguments.callee` lança `TypeError` uma vez que +> se tornou obsoleto. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/external/JavaScript-Garden/doc/ptbr/function/closures.md b/external/JavaScript-Garden/doc/ptbr/function/closures.md new file mode 100644 index 0000000..cdf8fd3 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/closures.md @@ -0,0 +1,83 @@ +## Closures e Referências + +Uma das caracterísricas mais poderosas do JavaScript é a possibilidade de usar *closures*. Quando usamos closures, definimos que um escopo **sempre** poderá acessar o escopo externo no qual foi definido. Uma vez que a única concepção de escopo em JavaScripe é [function scope](#function.scopes), todas as funções, por default, agem como closures. + +### Emulando variáveis privadas + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Aqui, `Counter` retorna **duas** closures: a função 'increment' bem como a função 'get'. Ambas as funções mantêm uma **referência** ao escopo de 'Counter' e, portanto, sempre mantêm o acesso à variável 'count' definida naquele escopo. + +### Por que variáveis privadas funcionam + +Uma vez que não é possível referenciar ou atribuir escopos em JavaScript, **não** existe uma maneira de acessar a variável 'count' por fora. A única maneira de interagir com a variável é através das duas closures. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +O código acima **não** irá mudar a variável 'count' no escopo de 'Counter', uma vez que 'foo.hack' não foi definido **naquele** escopo. Neste caso, uma variável 'global' 'count' será criada ou substituida. + +### Closures dentro de laços + +Um erro comum é utilizar closures dentro de laços, como se elas copiassem o valor da variável de indexação do laço. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +O exemplo acima **não** retornará os números '0' até '9', mas os número '10' dez vezes. + +A função *anônima* mantêm uma **referência** para 'i'. No momento em que 'console.log' é chamado, 'o laço for' já encerrou a execução, e o valor '10' está atrbuído em 'i'. + +Com a finalidade de se obter o comportamento esperado, é necessário criar uma **cópia** do valor de 'i'. + +### Evitando problemas de referência + +Com a finalidade de copiar o valor da variável de indexação do laço, a melhor opção é utilizar um [wrapper anônimo](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +A função anônima será chamada imediatamente com 'i' como seu primeiro argumento e receberá uma cópia do **valor** de 'i' como parâmetro de 'e'. + +A função anônima que é passada para o 'setTimeout' agora possui uma referência a 'e', cujo os valores **não** são modificados pelo laço. + +Não existe outra maneira de se obter este resultado, que não seja retornando uma função do wrapper anônimo que terá, então, o mesmo comportamento que o código acima. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +Há ainda uma outra maneira, usando .bind, que pode ligar argumentos e um contexto'this' a uma função. Isto se comporta como o código acima + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } diff --git a/external/JavaScript-Garden/doc/ptbr/function/constructors.md b/external/JavaScript-Garden/doc/ptbr/function/constructors.md new file mode 100644 index 0000000..26abfed --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/constructors.md @@ -0,0 +1,115 @@ +## Construtores + +Construtores em JavaScript ainda são diferentes de muitas outras linguagens. +Qualquer chamada a uma função que seja precedida pela palavra-chave `new` age como um cosntrutor. + +Dentro do construtor - a função chamada - o valor de `this` se refere ao objeto recém criado. +O [prototype](#object.prototype) deste **novo** objeto é definido como o `prototype` do objeto da função que foi +invocada como construtor. + +Se a função chamada não possui um `return` statement explícito, então implicitamente +retornará o valor de `this` - o novo objeto. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +O código acima chama `Foo` como construtor e define o `prototype` do objeto recém criado +como `Foo.prototype`. + +No caso de um `return` statement explícito, a função retorna o valor +especificado pelo statement, mas **somente** se o valor de retorno for um `Object`. + + function Bar() { + return 2; + } + new Bar(); // um novo objeto + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // o objeto retornado + +Quando a palavra-chave `new` é omitida, a função **não** retornará o novo objeto. + + function Foo() { + this.bla = 1; // esta definida no escopo global do objeto + } + Foo(); // undefined + +Enquanto que o exemplo acima pareça funcionar em alguns casos, devido +a maneira como [`this`](#function.this) funciona em JavaScript, o *objeto global* +será usado como valor do `this`. + +### Fábricas + +A fim de omitir a palavra-chave `new`, o construtor da função deve retornar um valor explicitamente. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Ambas as chamadas a `Bar` retornam a mesma coisa, um objeto recém criado +que tem a propriedade chamada `method`, que é uma [Closure](#function.closures). + +Deve-se perceber que a chamada `new Bar()` **não** afeta o prototype do objeto retornado. +Enquanto o prototype é definido no objeto recém criado, `Bar` nunca retornará um novo objeto. + +No exemplo acima, não existe diferença funcional entre o uso ou não de `new`. + +### Criando novos objetos por fábricas + +É recomendado **não** usar `new` pois eventual o esquecimento de seu uso +pode levar à defeitos. + +A fim de criar um novo objeto, deve-se utilizar uma fábrica e construir o novo objeto dentro desta fábrica. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +Enquanto que o código acima previne defeitos decorrentes do esquecimnto da palavra-chave `new` +e certamente utiliza-se de [private variables](#function.closures) de forma mais fácil, este apresenta algumas desvantagens: + + 1. Utiliza mais memória desde que os objetos criados **não** compartilham métodos em um prototype. + 2. A fim de implementar herença, a fábrica precisa copiar todos os métodos de um outro objeto ou colocar o outro objeto no prototype do novo objeto. + 3. Quebrar a cadeia prototype somente por causa de esquecer eventualmente o `new` vai contra o que é proposto pela linguagem. + +### Conclusão + +Enquanto que a omissão do `new` origine defeitos, **não** é certamente uma razão para +quebrar a estrura prototype como um todo. No final, a melhor solução é sempre a que se adequa às necessidades de cada projeto. +O importante é utilizar de forma **consistente** o modelo de criação de objetos escolhido. + diff --git a/external/JavaScript-Garden/doc/ptbr/function/general.md b/external/JavaScript-Garden/doc/ptbr/function/general.md new file mode 100644 index 0000000..dc002e8 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/general.md @@ -0,0 +1,46 @@ +## Declaração de funções e expressões + +Funções em JavaScript são objetos de primeira classe. Isto significa que elas +podem ser tratadas como qualquer outro tipo. Um uso muito comum desta característica é +o de passar uma *função anônima* como uma callback para outra, talvez uma função assíncrona. + +### A declaração `function` + + function foo() {} + +A função acima sofrerá [hoisting](#function.scopes) antes que a execução do programa se inicie; assim, +ela estará disponível em *todo* o escopo em que foi *definida*, até mesmo se for chamada antes de ter +sido definida no código. + + foo(); // Funciona pois foo foi elevada para o topo do escopo + function foo() {} + +### A expressão `function` + + var foo = function() {}; + +Este exemplo atribui uma função sem nome e *anônima* à variável `foo`. + + foo; // 'undefined' + foo(); // dispara TypeError + var foo = function() {}; + +Devido ao fato de que `var` é uma declaração que eleva a definição da variável `foo` ao topo do escopo, esta sofrerá hoist e `foo` estará declarado logo que o script for executado. + +Como atribuições só ocorrem em tempo de execução, o valor default de `foo` +será [undefined](#core.undefined) antes que o código seja executado. + +### Expressão de uma função nomeada + +Outro caso especial é a atribuição de funções nomeadas. + + var foo = function bar() { + bar(); // Funciona + } + bar(); // ReferenceError + +Aqui, `bar` não está disponível fora do escopo, uma vez que a função se encontra atribuída +em `foo`; no entanto, dentro de `bar`, ela esta disponível. Isto ocorre devido ao fato de +como o [name resolution](#function.scopes) funciona em JavaScript, o nome da função está *sempre* +disponível no escopo local da própria função. + diff --git a/external/JavaScript-Garden/doc/ptbr/function/scopes.md b/external/JavaScript-Garden/doc/ptbr/function/scopes.md new file mode 100644 index 0000000..0bcd99d --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/scopes.md @@ -0,0 +1,225 @@ +## Escopos e Namespaces + +Embora o JavaScript lide bem com a sintaxe de duas chaves para definir blocos, ele **não** oferece suporte a escopos em blocos; por isso, +todo o restante da linguagem é definido em *escopo de função*. + + function test() { // define escopo + for(var i = 0; i < 10; i++) { // não define escopo + // count + } + console.log(i); // 10 + } + +> **Nota:** Quando não usado em um assignment, em um statement return ou como um argumento de função, +> a notação `{...}` será interpretada como um block statement e **não** como um objeto literal. +> Isto, em conjunto com a [inserção automática de ponto-e-vírgula](#core.semicolon), pode levar à erros sutis. + +Também não existem namespaces distintos em JavaScript, o que significa que tudo é +automaticamente definido em um namespace *globalmente compartilhado*. + +Cada vez que uma variável é referenciada, o JavaScript vai percorrer toda a hierarquia +de escopos até encontrá-la. Caso ele alcance o escopo global e ainda não tenha encontrado +a variável, ele lançará um `ReferenceError`. + +### A queda das variáveis globais + + // script A + foo = '42'; + + // script B + var foo = '42' + +O dois scripts acima **não** têm o mesmo efeito. O Script A define uma +variável chamada `foo` no escopo *global*, e o Script B define `foo` no +escopo *atual*. + +Novamente, isto **não** é *mesma coisa*: emitir o uso de `var` tem várias implicações. + + // escopo global + var foo = 42; + function test() { + // escopo local + foo = 21; + } + test(); + foo; // 21 + +Deixando o statement `var` de fora da função `test` faz com que o valor de `test` seja sobrescrito. +Enquanto que à primeira vista isto não pareça um problema, um script com milhares de linhas de código +que não utiliza `var` apresenta erros horríveis e bugs difíceis de serem detectados. + + // escopo global + var items = [/* uma lista qualquer */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // escopo de subLoop + for(i = 0; i < 10; i++) { // esquecendo do var statement + // faça algo incrível! + } + } + +O loop externo terminará depois da primeira chamada para `subLoop`, uma vez que `subLoop` +sobrescreve o valor global `i`. Utilizar `var` no segundo `for` loop evitaria facilmente este problema. +O `var` statement **nunca** pode ser esquecido a não ser que o *efeito desejado* seja afetar o escopo externo. + +### Variáveis locais + +A única fonte de variáveis locais em JavaScript são parâmetros de [função](#function.general) +e variáveis declaradas via `var` statement. + + // escopo global + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // escopo local da função test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +Enquanto que `foo` e `i` são variáveis locais dentro do escopo da função `test`, +a atribuição de `bar` irá substituir a variável global com o mesmo nome. + +### Hoisting + +Javascript **eleva** declarações. Isto quer dizer que ambas declarações `var` +e `function` serão movidas para o topo do escopo ao qual pertencem. + + 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]; + } + } + +O código acima é modificado antes mesmo que seja executado. O JavaScript move + todos as declarações `var` assim como as de `function`, para o topo +do escopo mais próximo. + + // declarações var são movidas aqui + var bar, someValue; // default para 'undefined' + + // as declarações de função também são movidas + function test(data) { + var goo, i, e; // escopo de bloco ausente move essas variáveis p/ cá + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // falha com um TypeError uma vez que bar continua 'undefined' + someValue = 42; // atribuições não são afetadas pelo hoisting + bar = function() {}; + + test(); + +A falta de delimitação de um escopo não somente moverá `var` statements para fora de loops + e seus blocos, isto também fará com que os testes de determinados `if` se tornem não-intuitivos. + + No código original, embora o `if` statement pareça modificar a *variável global* + `goo`, ele modifica a *variável local* - depois que hoisting foi aplicado. + +Por desconhecer o *hoisting*, pode-se suspeitar que o código abaixo lançaria um +`ReferenceError`. + + // verifique se SomeImportantThing já foi inicializado + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Mas é claro, isto funciona devido ao fato de que `var` statement é movido para o topo +do *escopo global*. + + var SomeImportantThing; + + // outro código pode inicializar SomeImportantThing aqui, ou não + + // tenha certeza de que isto foi inicializado + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Ordem da Resolução de Nomes + +Todos os escopos em JavaScript, incluindo o *escopo global*, possuem o [`this`](#function.this) + o qual faz referência ao *objeto atual*. + +Escopos de funções também possuem o [`arguments`](#function.arguments), o qual contêm +os argumentos que foram passados para a função. + +Por exemplo, ao tentar acessar a variável denominada `foo` dentro do escopo de uma função, JavaScript irá +procurar pela variável na seguinte ordem: + + 1. No caso de haver `var foo` statement no escopo atual, use-a. + 2. Se um dos parâmetros é denominado `foo`, use-o. + 3. Se a própria função é denominada `foo`, use-a. + 4. Vá para o escopo externo mais próximo e inicie do **#1**. + +> **Nota:** Dispor de um parâmetro denominado `arguments` irá **previnir** a criação +> do objeto default `arguments`. + +### Namespaces + +Um problema comum relacionado ao fato de dispor de apenas um namespace global é +a probabilidade de esbarrar com problemas onde nomes de variáveis coincidem. Em JavaScript, +este problema pode ser facilmente evitado com a ajuda de *wrappers anônimos*. + + (function() { + // um "namespace" autocontido + + window.foo = function() { + // closure exposta + }; + + })(); // execute a função imediatamente + + +Funções sem nome são consideradas [expressões](#function.general); a fim de ser referênciável, +elas devem ser avaliadas. + + ( // avalie a função dentro dos parênteses + function() {} + ) // e retorne o objeto de função + () // chama o resultado da avaliação + +Existem outras maneiras para avaliar e chamar diretamente a expressão da função a qual, +enquanto que diferentes em sintaxe, comportam-se da mesma maneira. + + // Alguns outros estilos para invocar diretamente ... + !function(){}() + +function(){}() + (function(){}()); + // e assim por diante... + +### Conclusão + +É recomendado sempre utilizar um *wrapper anônimo* para encapsular código em seu +próprio namespace. Isto não é somente uma maneira de se proteger contra conflitos de nomes, +como também contribui para melhor modularização de programas. + +Adicionalmente, o uso de variáveis globais é considerado **uma prática ruim**. **Qualquer** +uso delas indica código mal escrito que tende à apresentar erros e apresenta manutenção complexa. + diff --git a/external/JavaScript-Garden/doc/ptbr/function/this.md b/external/JavaScript-Garden/doc/ptbr/function/this.md new file mode 100644 index 0000000..daacb9a --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/function/this.md @@ -0,0 +1,108 @@ +## Como funciona o `this` + +JavaScript tem uma concepção diferente sobre a que a palavra reservada `this` se refere da maioria das outras linguagens de programação. Existem exatamente **cinco** diferentes maneiras as quais os valores de `this` podem ser referenciados na linguagem. + +### O escopo Global + + this; + +Quando usando `this` no escopo global, ele simplesmente estará apontando para o objeto *global*. + + +### Chamando uma função + + foo(); + +Aqui, `this` irá referenciar novamente o objeto *global*. + +> **Nota ES5:** No strict mode, global **não existe**. +> neste caso, `this` receberá como valor `undefined`. + +### Chamando um método + + test.foo(); + +Neste exemplo, `this` irá referenciar `test`. + +### Chamando um construtor + + new foo(); + +Uma chamada de função que é precedida pela palavra chave `new` age como +um [construtor](#function.constructors). Dentro da função, `this` irá se referir +a um objeto *recém criado*. + +### Referência explícita do `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 + +Quando utiliza-se os métodos `call` ou `apply` de `Function.prototype`, o valor de +`this` dentro da função chamada irá referenciar **explicitamente** o primeiro argumento +correspondente na chamada da função. + +Como resultado, no exemplo anterior o *escopo original do método* **não** é aplicado, e `this` +dentro de `foo` irá referenciar `bar`. + +> **Nota:** `this` **não** pode ser utilizado para referenciar o objeto dentro de um `Object` lietral. +> Logo `var obj = {me: this}` **não** irá resultar em `me` apontando para +> `obj`, uma vez que `this` só pode ser referenciado em dos cinco casos aqui apresentados. + +### Erros comuns + +Embora a maioria destes casos façam sentido, o primeiro pode ser considerado +como um engano de concepção da linguagem, já que **nunca** se mostrou útil. + + Foo.method = function() { + function test() { + // this referencia o objeto global + } + test(); + }; + +Um erro comum é achar que `this` dentro de `test` referencia `Foo`; enquanto que, na realidade + **não é isto que acontece**. + +Com a finalidade de acessar `Foo` de dentro de `test`, é necessário instanciar +uma variável global dentro do método para se referir à `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Utilize that no lugar de this aqui + } + test(); + }; + +`that` trata-se de uma variável normal, porém é normalmente utilizada para referências externas de `this`. +Quando combinadas com [closures](#function.closures), também podem ser utilizadas para repassar `this` como valor. + +### Atribuindo métodos + +Outra coisa que **não** funciona em JavaScript é function aliasing, ou seja, +**atribuir** um método a uma variável. + + var test = someObject.methodTest; + test(); + +Devido ao primeiro caso, `test` se comportará como uma chamada de função; como consequencia, +o `this` dentro do método não apontará para `someObject`. + +Enquanto que realizar binding do `this` pareça uma idéia ruim, no fundo, é o que faz a +[herança prototypal](#object.prototype) funcionar. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Quando os métodos da instância de `Bar` são chamados, o `this` faz referência +àquela mesma instância. + + diff --git a/external/JavaScript-Garden/doc/ptbr/index.json b/external/JavaScript-Garden/doc/ptbr/index.json new file mode 100644 index 0000000..e7b22d3 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden em Português", + "description": "Um guia pelas vantagens e desvantagens do JavaScript.", + "sections": [ + { + "title": "Introdução", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Objetos", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Funções", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Arrays", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Tipos", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Core", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Outros assuntos", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/ptbr/intro/index.md b/external/JavaScript-Garden/doc/ptbr/intro/index.md new file mode 100644 index 0000000..ecba225 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/intro/index.md @@ -0,0 +1,38 @@ +## Introdução + +**JavaScript Garden** é uma coletânea crescente que documenta as peculiaridades da linguagem de programação JavaScript. Nela você encontra recomendações para escapar dos erros comuns aos mais sutís, bem como de problemas de desempenho e práticas ruins, que programadores novatos podem acabar encontrando enquanto se aprofundam na linguagem. + +JavaScript Garden **não** tem como propósito te ensinar à programar em JavaScript. Conhecimento prévio da linguagem é fortemente recomendado para que você entenda o conteúdo dos tópicos abordados neste guia. A fim de aprender as noções básicas da linguagem, por favor, consulte o excelente [guia][1] disponível na Mozilla Developer Network. + +## Os autores + +Este guia é fruto do trabalho de dois excelentes usuários do [Stack Overflow][2], [Ivo Wetzel][3] +(Conteúdo) e [Zhang Yi Jiang][4] (Design). + +É mantido atualmente por [Tim Ruffles](http://truffles.me.uk). + +## Contribuidores + +- São muitos para serem listados, [veja a lista completa clicando aqui](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors). + + +## Hospedagem + +JavaScript Garden está hospedado no GitHub, porém [Cramer Development][7] nos apoia com um espelho em [JavaScriptGarden.info][8]. + +## Licença + +JavaScript Garden está publicado sob a [licença MIT][9] e hospedado no [GitHub][10]. Caso você encontre defeitos ou erros de digitação, por favor [registre o problema][11] ou realize um pull request no repositório. Você também pode nos encontrar na [sala de JavaScript][12] no chat do Stack Overflow. + +[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/external/JavaScript-Garden/doc/ptbr/object/forinloop.md b/external/JavaScript-Garden/doc/ptbr/object/forinloop.md new file mode 100644 index 0000000..e3e09ef --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/object/forinloop.md @@ -0,0 +1,41 @@ +## O laço `for in` + +Assim como o operador `in`, o laço `for in` percorre a cadeia prototype quando interage sobre as propriedades de um objeto. + +> **Nota:** O laço `for in` **não** interage sobre propriedades que +> que tenham o atributo `enumerable` configurado como `false`; por exemplo, a propriedade `length` de um array. + + // Poluindo o Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // retorna ambos bar e moo + } + +Uma vez que não é possível alterar o comportamento do laço `for in` por si só, faz-se necessário filtrar as propriedades do objeto durante o ciclo de repetição do laço; isso é feito usando o método [`hasOwnProperty`](#object.hasownproperty) do `Object.prototype`. + +> **Nota:** Uma vez que o `for in` percorre toda a cadeia prototype, +> cada camada a mais na herança do objeto deixa a execução do laço mais lenta. + +### Utilizando `hasOwnProperty` como filtro + + // o mesmo foo utilizado anteriormente + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Esta é única forma correta de usar. Devido ao uso de `hasOwnProperty`, o exemplo **só** irá retornar `moo`. Quando `hasOwnProperty` é deixado de lado, o código fica propenso a erros nos casos em que o prototype - por exemplo `Object.prototype`- tenha sido estendido. + +Um framework largamente utilizado que estende o `Object.prototype` é [Prototype][1]. +Quando este framework é utilizado, laços `for in` que não utilizam +`hasOwnProperty` ficam desprotegidos contra erros. + +### Conclusão + +Recomenda-se utilizar `hasOwnProperty` **sempre**. Nunca faça pressuposições sobre o ambiente em que o código está sendo executado, ou se os prototypes nativos foram estendidos ou não. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/ptbr/object/general.md b/external/JavaScript-Garden/doc/ptbr/object/general.md new file mode 100644 index 0000000..12452e7 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/object/general.md @@ -0,0 +1,83 @@ +## Propriedades e manipulação de objetos + +Tudo em JavaScript se comporta como um objeto, com apenas duas exceções que são +[`null`](#core.undefined) e [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Um equívoco muito comum é a idéia de que números não podem ser manipulados como objetos. O parser do JavaScript analisa a *notação de ponto* como ponto flutuante de um número. + + 2.toString(); // raises SyntaxError + +Existem três soluções para contornar este problema e permtir que números se comportem como objetos. + + 2..toString(); // o segundo ponto é reconhecido corretamente + 2 .toString(); // perceba o espaço deixado à esquerda do ponto + (2).toString(); // 2 é interpretado primeiro + +### Objetos como tipo de dados + +Em JavaScript, Objetos podem também ser utilizados como [*Hashmaps*][1]; eles consistem principalmente de propriedades nomeadas, que apontam para valores. + +Usando um objeto literal - notação do tipo `{}`- é possível criar um objeto simples. Este novo objeto [herda](#object.prototype) de `Object.prototype` e não possui [propriedades próprias](#object.hasownproperty) definidas. + + var foo = {}; // um novo objeto vazio + + // um novo objeto com uma propriedade 'test' populada com o valor 12 + var bar = {test: 12}; + +### Acessando propriedades + +As propriedades de um objeto podem ser acessadas de duas maneiras, através da notação de ponto ou da notação de colchete. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // Erro de sintaxe + foo['1234']; // funciona + +Ambas as notações trabalham de forma quase idêntica, com a única diferença de que o colchete permite configuração dinâmica de propriedades e uso de propriedades nomeadas que de outra maneira levaria à erros de sintaxe. + +### Removendo propriedades + +A única maneira de remover uma propriedade de um objeto é através do operador `delete`; definir uma propriedade como `undefined` ou `null` somente apaga o valor associado com tal propriedade, mas não remove a *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]); + } + } + +O código acima retorna tanto `bar undefined` quanto`foo null` - somente `baz` foi removido e, portanto, não saiu no output. + +### Notações de Keys + + var test = { + 'case': 'I am a keyword, so I must be notated as a string', + delete: 'I am a keyword, so me too' // dispara SyntaxError + }; + +Propriedades de objetos podem ser tanto representadas como caracteres simples bem como strings. Devido a outro engano do parser do JavaScript, o exemplo acima irá retornar `SyntaxError` remetendo ao ECMAScript 5. + +Este erro decorre do fato de que 'apagar' é uma *palavra reservada*; por consequencia, deve ser representada como uma *string literal* a fim de garantir a interpretação correta pelas antigas engines de JavaScript. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/ptbr/object/hasownproperty.md b/external/JavaScript-Garden/doc/ptbr/object/hasownproperty.md new file mode 100644 index 0000000..858e052 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/object/hasownproperty.md @@ -0,0 +1,56 @@ +## `hasOwnProperty` + +Para verificar se uma propriedade está definida no **próprio** objeto e não em outro lugar +da sua [cadeia prototype](#object.prototype), é necessário utilizar o método +`hasOwnProperty` o qual todos os objetos herdam de `Object.prototype`. + +> **Nota:** **Não** é sufuciente verificar se uma propriedade é `undefined`. +> A propriedade pode muito bem existir, porém acontece de seu valor só ser +> `undefined`. + +`hasOwnProperty` é a única coisa em JavaScript a qual lida com propriedades e **não** percorre a cadeia prototype. + + // Poluindo 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 + +Somente `hasOwnProperty` irá retornar o resultado correto e esperado; isto é +essencial quando se interage sobre propriedades de qualquer objeto. **Não** existe +outra maneira de verificar propriedades que não estejam definidas no próprio objeto, mas +em outro lugar na cadeia prototype. + +### `hasOwnProperty` como propriedade + +JavaScript não protege o nome da propriedade `hasOwnProperty`; assim, se +existe a possibilidade de algum objeto possuir uma propriedade com este mesmo nome, +torna-se necessário utilizar um `hasOwnProperty` **externo** a fim de obter resultados corretos. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // sempre retorna false + + // Utiliza hasOwnProperty de outro objeto e o instancia com 'this' apontado para foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // Também é possível utilizar hasOwnProperty do Object + // prototype para este fim + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + + +### Conclusão + +O método `hasOwnProperty` é a **única** maneira confiável para verificar a existência da propriedade em um objeto. +É recomendado que `hasOwnProperty` seja utilizado em **cada** interação de um [laço `for in`](#object.forinloop) +a fim de evitar erros de extensão do [prototype](#object.prototype). + diff --git a/external/JavaScript-Garden/doc/ptbr/object/prototype.md b/external/JavaScript-Garden/doc/ptbr/object/prototype.md new file mode 100644 index 0000000..c941164 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/object/prototype.md @@ -0,0 +1,109 @@ +## Prototype + +JavaScript não dispõe de nenhum modelo clássico de herança; em vez disso, ele +faz uso do modelo *prototypal*. + +Enquanto isto é considerado muitas vezes como sendo um dos pontos fracos do JavaScript, o modelo de herança prototypal é de fato muito mais poderoso do que o modelo clássico. +Por exemplo, isto torna relativamente trivial construir um modelo clássico +com base no modelo prototypal, enquanto que o contrário se verifica como uma tarefa mais difícil. + +JavaScript é a única linguagem amplamente utilizada que apresenta um modelo de herança do tipo prototypal, +por isso pode levar algum tempo até que você se ajuste às diferenças entre os dois modelos. + +A primeira grande diferença é que herança em JavaScript utiliza o conceito de *cadeias prototype*. + +> **Nota:** Usando simplesmente `Bar.prototype = Foo.prototype` resultará em ambos os objetos +> compartilhando **o mesmo** prototype. Portanto, as alterações no prototype de um dos objetos +> também irá afetar o prototype do outro, o que na maioria dos casos não é o esperado. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Apontar Bar's prototype para uma nava instância de Foo + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Tenha certeza de que Bar é o construtor atual + Bar.prototype.constructor = Bar; + + var test = new Bar(); // criar uma nova instância de bar + + // A cadeia prototype resultante + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +No código acima, o objeto `test` irá herdar de ambos `Bar.prototype` e +`Foo.prototype`; portanto, ele terá acesso à função `method` que foi definida em Foo. +Ele também terá acesso à propriedade `value` da **única** instância de Foo que é seu próprio prototype. +É importante perceber que `new Bar()` não cria uma nova instância de `Foo`, mas +reutiliza aquela associada ao prototype; assim, todas as intâncias `Bar` dividirão a +**mesma** propriedade `value`. + +> **Nota:** **Não** utilize `Bar.prototype = Foo`, uma vez que isto não aponta para o prototype de `Foo`, mas sim para o objeto função `Foo`. +> Assim a cadeia prototype irá percorrer `Function.prototype` e não `Foo.prototype`; +> desse modo, `method` não estará na cadeia prototype. + +### Buscando propriedades + +Ao acessar as propriedades de um objeto, JavaScript irá percorre a cadeia prototype +**até o topo** para encontrar a propriedade solicitada. + +Caso atinja o topo da cadeia - denominada `Object.prototype` - e não encontre +a propriedade especificada, o valor [undefined](#core.undefined) será retornado. + +### A propriedade Prototype + +Enquanto a propriedade prototype é utilizada pela linguagem na contrução de cadeia de prototype, +ainda é possível associar **qualquer** valor dado a ele. No entanto, tipos primitivos serão +ignorados quando associados como prototype. + + function Foo() {} + Foo.prototype = 1; // sem efeito + +Atribuindo objetos, como demonstrado no exemplo anterior, irá funcionar, e permite +a criação dinâmica de cadeias prototype. + +### Performance + +O tempo de pesquisa por propriedades que estão no topo da cadeia prototype +pode ter um impacto negativo na performance, principalmente em código +onde a performance é um fator crítico. Além disso, a busca por propriedades que não existem +também atravessa a cadeia prototype. + +Além disso, ao [interagir](#object.forinloop) com propriedades de um objeto +**cada** propriedade na cadeia prototype será enumerada. + +### Estendendo Prototypes nativos + +Uma prática ruim que é normalmente utilizada é a de estender `Object.prototype` ou qualquer outro prototype construído. + +Esta técnica é denominada [monkey patching][1] e quebra o *encapsulamento*. +Mesmo utilizada por frameworks populars como [Prototype][2], não existe mais razão +para poluir tipos built-in com funcionalidades adicionais *fora de padrão*. + +A **única** boa razão existente para continuar estendendo um built-in prototype +é a de assegurar as novas funcionalidade de engines JavaScript modernas; por exemplo, [`Array.forEach`][3]. + +### Conclusão + +É **essencial** entender o modelo de herança prototypal antes de escrever código complexo +que faço uso do mesmo. Além disso, tome cuidado com o tamanho da cadeia prototype em seu código +e a refatore caso necessário a fim de evitar futuros problemas de performance. A respeito do prototypes nativos, +estes **nunca** devem ser estendidos ao menos que seja para manter a compatibilidade com novas +características do JavaScript. + +[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/external/JavaScript-Garden/doc/ptbr/other/timeouts.md b/external/JavaScript-Garden/doc/ptbr/other/timeouts.md new file mode 100644 index 0000000..74ec208 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/other/timeouts.md @@ -0,0 +1,161 @@ +### `setTimeout` e `setInterval` + +Uma vez que JavaScript é assíncrono, é possível agendar a execução de uma função +usando as funções `setTimeout` e `setInterval`. + +> **Nota:** Timeouts **não** fazem parte do Padrão ECMAScript. Eles são +> implementados como parte do [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // retorna um Number > 0 + +Quando `setTimeout` é chamado, ele retorna o ID do timeout e agenda a execução de `foo` +para **aproximadamente** mil milissegundos no futuro. +`foo` será executado uma **única** vez. + +Dependendo de como a engine JavaScript que está rodando o código resolve o timer, bem como +o fato de que o JavaScript é single threaded e outro código que é executado pode bloquear a +thread, **não há como** garantir a precisão dos intervalos especificados nas chamadas `setTimeout`. + +A função que foi passada como primeiro parâmetro será chamada pelo *objeto global*, o que +significa que o [`this`](#function.this) dentro da função chamada se refere ao objeto global. + + function Foo() { + this.value = 42; + this.method = function() { + // this faz referência ao objeto global + console.log(this.value); // log undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Nota:** Como o `setTimeout` espera um **objeto de função** para primeiro parâmetro, um +> erro comum é usar `setTimeout(foo(), 1000)`, que irá usar o +> **o valor retornado** por `foo` e **não** `foo`. Isto é, na maioria das vezes, +> um erro silencioso, visto que neste caso a função retorna `undefined`, logo `setTimeout` **não** +> lançará erro algum. + +### Acumulando chamadas com o `setInterval` + +Enquanto que `setTimeout` somente executa a função uma vez, `setInterval` - como +o nome sugere - irá executar a função a **cada** `X` milisegundos, porém seu uso é +desencorajado. + +Quando um código em execução bloqueia a chamada do timeout, `setInterval` continuará +emitindo chamadas para a função em questão. Isto pode, especialmente com intervalos curtos, +resultar em uma pilha de chamadas de função. + + function foo(){ + // algo que bloqueie por 1 segundo + } + setInterval(foo, 1000); + +No código acima, `foo` será chamada uma vez e irá então bloquear a execução por um segundo. + +Enquanto `foo` bloqueia a execução, `setInterval` irá programar mais chamadas para ela. +Em seguida, quando `foo` completar sua execução, existirão **dez** chamadas programadas +para ela aguardando por execução. + +### Lidando com possíveis bloqueios de código + +A solução mais fácil, bem como a mais controlável, é usar `setTimeout` dentro da +própria função. + + function foo(){ + // Algo que bloqueia por um segundo + setTimeout(foo, 1000); + } + foo(); + +Isto não somente encapsula a chamada para `setTimeout`, mas também previne +o acumulo de chamadas e dá controle adicional. `foo` por si só pode decidir +quando rodar novamente ou não. + +### Limpando Timeouts manualmente + +A limpeza de intervalos e timeouts funciona passando o respectivo ID +para `clearTimeout` ou `clearInterval`, dependendo onde a função `set` foi usada primeiro. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Limpando todos os Timeouts + +Como não existe métodos próprios para limpar todos os timeouts e/ou intervalos, +é necessário usar a força bruta para chegar a esta funcionalidade. + + // limpe "todos" os timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Mas ainda podem haver timeouts que não serão afetados por este número arbitrário. +Uma outra maneira de fazer isto é considerar que o ID dado a um timeout é +incrementado um a um cada vez que você chama `setTimeout`. + + // limpe "todos" os timeouts + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +Apesar desta maneira funcionar nos principais navegadores hoje em dia, não está especificado +que os IDs respeitem uma ordem como este, logo esta ordem pode ser variada. Por este motivo, em vez disso +é recomendade manter o controle de todos os IDs de timeouts, de forma que possam ser apagados precisamente. + +### O uso oculto do `eval` + +`setTimeout` e `setInterval` aceitam uma string como primeiro argumento. +Esta funcionalidade **nunca** deve ser utilizada pois internamente faz uso de `eval`. + +> **Nota:** Uma vez que funções timeout **não** são especificadas pelo padrão ECMAScript, a maneira como +> eles interpretam uma string passada pode variar de acordo com a implementação do JavaScript. Por exemplo, JScript +> da Microsoft faz uso do construtor `Function` no lugar do `eval`. + + function foo() { + // será chamada + } + + function bar() { + function foo() { + // nunca será chamada + } + setTimeout('foo()', 1000); + } + bar(); + +Uma vez que `eval` não é chamado [diretamente](#core.eval) neste caso, a string +passada como argumento para `setTimeout` será executada no *escopo global*; assim, ela +não usará a variável local `foo` do escopo de `bar`. + +Também é recomendado **não** usar uma string para passar argumentos +para a função que será chamada por qualquer uma das funções de timeout. + + function foo(a, b, c) {} + + // NUNCA use isto + setTimeout('foo(1, 2, 3)', 1000) + + // Utilize uma função anônima do lugar + setTimeout(function() { + foo(a, b, c); + }, 1000) + +> **Nota:** Enquanto que é possivel utilizar a sintaxe +> `setTimeout(foo, 1000, a, b, c)`, não é recomendada, pois sua utilização pode levar +> a erros sútis quando utilizadas com [métodos](#function.this). + +### Conclusão + +Uma string **nunca** deve ser usada como parâmetro `setTimeout` ou +`setInterval`. Esta prática é um sinal **claro** de código ruim, quando argumentos precisam ser fornecido para a função que é chamada. +Uma *função anônima* é que deve ser passada para que, em seguida, cuide da chamada. + +Além disso, o uso de `setInterval` deve ser evitado pois seu scheduler não é +bloqueado pela execução do JavaScript. + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" + diff --git a/external/JavaScript-Garden/doc/ptbr/types/casting.md b/external/JavaScript-Garden/doc/ptbr/types/casting.md new file mode 100644 index 0000000..d7dc9b3 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/types/casting.md @@ -0,0 +1,67 @@ +## Conversão de tipos + +JavaScript é *fracamente tipado*, logo ele aplica a *coerção de tipos* +**sempre** que possível. + + // Estes retornam true + new Number(10) == 10; // Number.toString() é convertido + // de volta a um número + + 10 == '10'; // Strings são convertidas em Number + 10 == '+10 '; // Mais loucuras com strings + 10 == '010'; // E mais + isNaN(null) == false; // null é convertido em 0 + // que claro não é NaN + + // Estes retornam false + 10 == 010; + 10 == '-10'; + +> **Nota ES5:** Literais Number que começam com um `0` são interpretados como octais +> (Base 8). Suporte à octais para estes literais foi **removido** no modo estrito do ECMAScript. + +A fim de evitar os problemas acima, o uso do [operador de igualdade estrito](#types.equality) +é **fortemente** recomendado. Embora ele evite uma série de problemas comuns, +existem ainda muitas outras questões que surgem do fraco sistema de tipagem do JavaScript. + +### Construtores de tipos nativos + +Os construtores de tipos nativos como `Number` e `String` comportam-se +diferentemente quando utilizados com ou sem a palavra-chave `new`. + + new Number(10) === 10; // False, Object e Number + Number(10) === 10; // True, Number e Number + new Number(10) + 0 === 10; // True, devido à conversão implícita + +Utilizar um tipo nativo como `Number` como construtor iré criar um novo objeto `Number`, +porém omitir a palavra-chave `new` fará com que a função `Number` se comporte como +um conversor. + +Além, passando valores literais ou não-objetos irá resultar em mais coerções +de tipos. + +A melhor opção é converter para um dos três possíveis tipos **de forma explícita**. + +### Convertendo para String + + '' + 10 === '10'; // true + +Prefixando uma string vazia, qualquer valor pode ser facilmente convertido em uma string. + +### Convertendo para Number + + +'10' === 10; // true + +Ao utilizar o operador de soma **unário**, é possível converter um valor para Number. + +### Convertendo para Boolean + +Ao utilizar duas vezes o operador **not**, é possível converter um valor para Boolean. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/ptbr/types/equality.md b/external/JavaScript-Garden/doc/ptbr/types/equality.md new file mode 100644 index 0000000..709b118 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/types/equality.md @@ -0,0 +1,70 @@ +## Igualdades e comparações + +JavaScript tem duas maneiras diferentes de comparar a igualdades entre valores de objetos. + +### O operador de igualdade + +O operador de igualdade consiste de dois sinais de igual : `==` + +JavaScript é *fracamente tipado*. Isto que dizer que o operador de igualdade +**induz** tipos ao invés de compará-los. + + "" == "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 + +A tabela acima mostra o resultado da coerção de tipos, e isto é principal razão +para que o uso `==` seja amplamente considerado uma má prática. Seu uso introduz defeitos +difíceis de serem rastreados devido às suas complicadas regras de conversão. + +Adicionalmente, também existe um impacto em performance quando a coerção acontece; +por exemplo, é necessário que uma string seja convertida em um número antes que seja comparada +com outro número. + +### O operador de igualdade estrito + +O operador de igualdade estrito consiste de **três** sinais de igual : `===`. + +Ele funciona como o operador de igualdade normal, salvo que o operador de igualdade estrito +**não** realiza coerção de tipos entre seus operandos. + + "" === "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 + +Os resultados acima são bastante claros e permitem uma análise objetiva do código. Pode parecer complicar o código até um certo ponto + mas também traz ganhos de performance em casos em que os operandos são de tipos diferentes. + +### Comparando Objetos + +Enquanto que ambos `==` e `===` são denominados operadores de **igualdade**, eles se comportam de +formas diferentes quando pelo menos um de seus operandos é um `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Aqui, ambos os operadores comparam em função da **identidade** e **não** da igualdade; isto é, +eles vão comparar em função da mesma **instância** do objeto, muito parecido com o `is` do Python +e a comparação de ponteiros em C. + +### Conclusão + +E fortemente recomendado que só se use o operador de ** igualdade estrito**. +Em casos onde a coerção de tipos seja necessária, isto deve ser feito [explicitamente](#types.casting) +e não deve ser deixado para as complicadas regras de coerção da linguagem. + diff --git a/external/JavaScript-Garden/doc/ptbr/types/instanceof.md b/external/JavaScript-Garden/doc/ptbr/types/instanceof.md new file mode 100644 index 0000000..9cbd756 --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/types/instanceof.md @@ -0,0 +1,38 @@ +## O operador `instanceof` + +O operador `instanceof` compara os construtores de seus dois operandos. +Ele é útil somente quando estamos comparando objetos personalizados. Quando utilizado em tipos nativos, +ele é tão inútil quanto [o operador typeof](#types.typeof). + +### Comparando objetos personalizados + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Isto somente define Bar.prototype ao objeto de função Foo, + // mas não à instância atual de Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Utilizando `instanceof` com tipos nativos + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +Uma coisa importante para perceber aqui é que `instanceof` não funciona em objetos +originados de diferentes contextos de JavaScript (isto é, de diferentes documentos em um +navegador web), uma vez que seus construtores não irão representar exatamente o mesmo objeto. + +### Conclusão + +O operador `instanceof` deve **somente** ser utilizado quando estive lidando +com objetos customizados originados de um mesmo contexto JavaScript. Bem como o operador +[`typeof`](#types.typeof), qualquer outro uso de `instanceof` deve ser **evitado**. + diff --git a/external/JavaScript-Garden/doc/ptbr/types/typeof.md b/external/JavaScript-Garden/doc/ptbr/types/typeof.md new file mode 100644 index 0000000..3f499ee --- /dev/null +++ b/external/JavaScript-Garden/doc/ptbr/types/typeof.md @@ -0,0 +1,83 @@ +## O operador `typeof` + +O operador `typeof`(em conjunto com +[`instanceof`](#types.instanceof) é provavelmente a maior falha de design do JavaScript, +por estar **complemente mal implementado**. + +Embora `instanceof` ainda tenha seu uso limitado, `typeof` realmente só possui uma utilidade, +a qual **não** acaba por ser a de verificar o tipo de um objeto. + +> **Nota:** Enquanto que `typeof` possa também ser invocado com uma sintaxe parecida com a de chamada de função, i.e. +> `typeof(obj)`, não se trata de uma chamada de função. Os parênteses se comportam normalmente +> e o valor retornado será usado como o operando do operador `typeof`. +> **Não** existe a função `typeof`. + +### A tabela de tipos em JavaScript + + 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 + +Na tabela acima, *Type* se refere ao valor de retorno do operador `typeof`. +Como pode ser facilmente observado, este valor não é nada consistente. + +O *Class* se refere ao valor interno da propriedade `[[Class]]` de um objeto. + +> **Da especificação:** O valor de `[[Class]]` pode ser +> das seguintes strings. `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +A fim de se obeter o valor de `[[Class]]`, deve-se utilizar o método +`toString` de `Object.prototype`. + +### A classe de um objeto + +A especificação fornece exatamente uma maneira de acessar o valor de `[[Class]]`, +com o uso de `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 + +No exemplo acima, `Object.prototype.toString` é chamado enquanto que o valor de [this](#function.this) +é definido como o objeto o qual o valor `[[Class]]` deva ser retornado. + +> **Nota ES5:** Por conveniência o retorno do valor de `Object.prototype.toString` +> para ambos `null` e `undefined` foi **modificado** de `Object` para `Null` e +> `Undefined` no ECMAScript 5. + +### Teste para variáveis não-definidas + + typeof foo !== 'undefined' + +O exemplo acima irá verificar se `foo` foi declarado ou não; apenas +o fato de referênciá-lo poderia resultar em `ReferenceError`. Esta é a única utilidade +real de `typeof`. + +### Conclusão + +A fim de verificar o tipo de um objeto, é fortemente recomendade o uso de +`Object.prototype.toString` pelo motivo de que esta é a única maneira confiável de ser feita. +Como demonstrado na tabela anterior, alguns valores retornados de `typeof` não estão definidos na +especificação; assim, eles podem variar entre implementações. +O uso de `typeof` deve ser evitado, a menos que não se esteja testando se uma variável está ou não definida. + + diff --git a/external/JavaScript-Garden/doc/ru/appendix/fromtranslators.md b/external/JavaScript-Garden/doc/ru/appendix/fromtranslators.md new file mode 100644 index 0000000..c9c399d --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/appendix/fromtranslators.md @@ -0,0 +1,14 @@ +## От переводчиков + +Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript, рекомендуем *прислушиваться* к этим советам, но при этом не делать из этого *крайность*. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально. + +К примеру, в статье про [сравнение объектов](#types.equality) авторы настоятельно рекомендуют использовать *только* оператор строгого неравенства `===`. Но мы считаем, что если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ `=`. Вы вольны применять строгое неравенство только в случаях, когда вы не уверены в типах операндов (`!== undefined` — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности. + +Функцию [`setInterval`](#other.timeouts) тоже можно использовать, если вы стопроцентно уверены, что код внутри неё будет исполняться как минимум в три раза быстрее переданного ей интервала. + +С другой стороны, [использование `var`](#function.scopes) и грамотная [расстановка точек с запятой](#core.semicolon) — обязательные вещи, халатное отношение к которым никак не может быть оправдано — в осознанном пропуске `var` (если только вы не переопределяете глобальный объект браузера... хотя *зачем*?) или точки с запятой нет никакого смысла. + +Относитесь с **мудростью** к тому, что вы пишете — *важно* знать, как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. *Важно* знать, как работает [прототипное наследование](#object.prototype), но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант. + +Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах, или у вас один из тех супер-крупных проектов, которым никогда не помешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест. + diff --git a/external/JavaScript-Garden/doc/ru/array/constructor.md b/external/JavaScript-Garden/doc/ru/array/constructor.md new file mode 100644 index 0000000..13f5884 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/array/constructor.md @@ -0,0 +1,25 @@ +## Конструктор `Array` + +Так как в конструкторе `Array` есть некоторая двусмысленность, касающаяся его параметров, настоятельно рекомендуется при создании массивов всегда использовать синтаксис литеральной нотации — `[]`. + + [1, 2, 3]; // Результат: [1, 2, 3] + new Array(1, 2, 3); // Результат: [1, 2, 3] + + [3]; // Результат: [3] + new Array(3); // Результат: [] + new Array('3') // Результат: ['3'] + +В случае, когда в конструктор `Array` передаётся один аргумент и этот аргумент имеет тип `Number`, конструктор возвращает новый, *заполненный случайными значениями*, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство `length` нового массива, индексы массива фактически не будут проинициализированы. + + var arr = new Array(3); + arr[1]; // не определён, undefined + 1 in arr; // false, индекс не был установлен + +Поведение, которое позволяет изначально установить только размер массива, может пригодиться лишь в нескольких случаях, таких как повторение строк, за счёт чего избегается использование цикла `for`. + + new Array(count + 1).join(stringToRepeat); + +### Заключение + +Использование конструктора `Array` нужно избегать, насколько это возможно. Литералы определённо предпочтительнее — это краткая запись и она имеет более понятный синтаксис, так что при этом даже улучшается читабельность кода. + diff --git a/external/JavaScript-Garden/doc/ru/array/general.md b/external/JavaScript-Garden/doc/ru/array/general.md new file mode 100644 index 0000000..a9012e9 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/array/general.md @@ -0,0 +1,40 @@ +##  Итерации по массивам и свойства + +Несмотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования [цикла `for in`](#object.forinloop) для итерации по элементам массива. Фактически, существует несколько весомых причин **против** использования `for in` в массивах. + +> **Замечание:** Массивы в JavaScript **не** являются *ассоциативными массивами*. Для связывания ключей и значений в JavaScript есть только [объекты](#object.general). И при том, что ассоциативные массивы **сохраняют** заданный порядок, объекты **не** делают этого. + +Во время выполнения `for in` циклически перебираются все свойства объекта, находящиеся в цепочке прототипов. Единственный способ исключить ненужные свойства — использовать [`hasOwnProperty`](#object.hasownproperty), а это **в 20 раз** медленнее обычного цикла `for`. + +### Итерирование + +Для достижения лучшей производительности при итерации по массивам, лучше всего использовать обычный цикл `for`. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: `l = list.length`. + +Несмотря на то, что свойство `length` определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Пусть в этом случае новые движки JavaScript теоретически **могут** применить оптимизацию, но нет никакого способа узнать, будет оптимизирован код на новом движке или нет. + +Фактически, отсутствие кэширования может привести к выполнению цикла в **два раза медленнее**, чем при кэшировании длины + +### Свойство `length` + +Хотя *геттер* свойства `length` просто возвращает количество элементов содержащихся в массиве, *сеттер* можно использовать для **обрезания** массива. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Присвоение свойству `length` меньшей величины урезает массив, однако присвоение большего значения не даст никакого эффекта. + +### Заключение + +Для оптимальной работы кода рекомендуется всегда использовать простой цикл `for` и кэшировать свойство `length`. Использование `for in` с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения. + diff --git a/external/JavaScript-Garden/doc/ru/core/delete.md b/external/JavaScript-Garden/doc/ru/core/delete.md new file mode 100644 index 0000000..7db3010 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/core/delete.md @@ -0,0 +1,88 @@ +## Оператор `delete` + +Если говорить коротко, то JavaScript *невозможно* удалить глобальную переменную, функцию или любой другой объект, которому задан атрибут `DontDelete` . + +### Глобальный код и код функции + +Если переменная или функция определена в глобальной области видимости (scope) или в +[области видимости функции](#function.scopes), это значит что она является +свойством (property) глобального или же Activation объекта. +Подобные свойства имеют набор атрибутов, одним из которых и является +упомянутый ранее `DontDelete`. Объявление переменных и функций в коде +функции или глобально всегда создает свойство с атрибутом `DontDelete`, и поэтому +не может быть удалено. + + // глобальная переменная: + var a = 1; // задается DontDelete + delete a; // false + a; // 1 + + // обычная функция: + function f() {} // задается DontDelete + delete f; // false + typeof f; // "function" + + // переназначение не поможет: + f = 1; + delete f; // false + f; // 1 + +### Явные свойства + +Явно заданные свойство могут быть удалены обычным способом. + + // явно заданные свойства: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +В примере выше, `obj.x` и `obj.y` могут быть удалены потому что у них не задан +атрибут `DontDelete`. Именно поэтому следующий пример тоже сработает. + + // работает хорошо везде, кроме IE: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - просто глобальная переменная + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Здесь мы используем небольшой трюк, чтобы удалить `a`. [`this`](#function.this) здесь +относится к глобальному объекту и мы явно указали удаление переменной `a` как свойства +глобального объекта, что и позволи нам ее удалить. + +Из-за нескольких багов в IE (как минимум 6-8) предыдущий код работать в нем не будет. + +### Аргументы функций и встроенные модули (built-ins) + +Обычным аргументам функций [`arguments` objects](#function.arguments) и +встроенным свойствам также задан атрибут `DontDelete`. + + // аргументы функции и свойства: + (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 объекты + +Host объект - это объект, предоставляемый окружением. К таким объектам относятся `window`, +`document`, `location` и так далее. +Для host объектов поведение оператора `delete` может быть непредсказуемым. +Согласно спецификации, таким объектам позволено реализовывать любой вид поведения. + +### Заключение + +Оператор `delete` часто обладает непредсказуемым поведением и безопасно использовать +его можно лишь для удаления явно заданных свойств обычных объектов. \ No newline at end of file diff --git a/external/JavaScript-Garden/doc/ru/core/eval.md b/external/JavaScript-Garden/doc/ru/core/eval.md new file mode 100644 index 0000000..8051a5a --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/core/eval.md @@ -0,0 +1,39 @@ +## Почему нельзя использовать `eval` + +Функция `eval` выполняет строку кода JavaScript в локальной области видимости. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Но `eval` исполняется в локальной области видимости только тогда, когда он вызывается **напрямую** *и при этом* имя вызываемой функции именно `eval`. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +**Любой ценой** избегайте использования функции `eval`. 99.9% случаев её "использования" могут достигаться **без её участия**. + +### `eval` под прикрытием + +Обе [функции работы с интервалами времени](#other.timeouts) `setTimeout` и `setInterval` могут принимать строку в качестве первого аргумента. Эта строка **всегда** будет выполняться в глобальной области видимости, поскольку `eval` в этом случае вызывается *не напрямую*. + +### Проблемы с безопасностью + +Кроме всего прочего, функция `eval` — это проблема в безопасности, поскольку исполняется **любой** переданный в неё код; **никогда** не следует использовать её со строками из неизвестных или недоверенных источников. + +### Заключение + +Никогда не стоит использовать `eval`: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходима `eval`, эта часть должна тут же ставиться под сомнение и **не** должна использоваться в первую очередь — необходимо найти *лучший способ*, которому не требуются вызовы `eval`. + diff --git a/external/JavaScript-Garden/doc/ru/core/semicolon.md b/external/JavaScript-Garden/doc/ru/core/semicolon.md new file mode 100644 index 0000000..e36be8d --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/core/semicolon.md @@ -0,0 +1,101 @@ +## Автоматическая вставка точек с запятой + +Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом **не** принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить. + +При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript **автоматически** вставляет их в те места, где сталкивается с ошибкой парсинга из-за их отсутствия. + + var foo = function() { + } // ошибка разбора, ожидается точка с запятой + test() + +Происходит вставка и парсер пытается снова. + + var foo = function() { + }; // ошибки нет, парсер продолжает + test() + +Автоматическая вставка точек с запятой считается одним из **наибольших** упущений в проекте языка, поскольку она *может* изменить поведение кода. + +### Как это работает + +Приведённый код не содержит точек с запятой, так что места для их вставки остаются на совести парсера: + + (function(window, undefined) { + function test(options) { + log('тестируем!') + + (options.list || []).forEach(function(i) { + + }) + + options.value.test( + 'здесь передадим длинную строчку', + 'и ещё одну на всякий случай' + ) + + return + { + foo: function() {} + } + } + window.test = test + + })(window) + + (function(window) { + window.someLibrary = {} + + })(window) + +Ниже представлен результат игры парсера в "угадалки". + + (function(window, undefined) { + function test(options) { + + // не вставлена точка с запятой, строки были объединены + log('тестируем!')(options.list || []).forEach(function(i) { + + }); // <- вставлена + + options.value.test( + 'здесь передадим длинную строчку', + 'и ещё одну на всякий случай' + ); // <- вставлена + + return; // <- вставлена, в результате + // оператор return разбит на два блока + { // теперь парсер считает этот блок отдельным + + // метка и одинокое выражение + foo: function() {} + }; // <- вставлена + } + window.test = test; // <- вставлена + + // снова объединились строки + })(window)(function(window) { + window.someLibrary = {}; // <- вставлена + + })(window); //<- вставлена + +> **Замечание:** Парсер JavaScript некорректно обрабатывает оператор `return`, за которым следует новая строка; кстати, причина может быть и не в автоматической вставке точек с запятой, но это в любом случае нежелательный побочный эффект + +Парсер радикально подменил поведение изначального кода, а в определённых случаях он сделал **абсолютно неправильные выводы**. + +### "Висящие" скобки + +Если парсер встречает "висящую" скобку, то он **не** вставляет точку с запятой. + + log('тестируем!') + (options.list || []).forEach(function(i) {}) + +Такой код трансформируется в строку + + log('тестируем!')(options.list || []).forEach(function(i) {}) + +**Чрезвычайно** высоки шансы, что `log` возвращает **не** функцию; таким образом, эта строка вызовет `TypeError` с сообщением о том, что `undefined не является функцией`. + +### Заключение + +Настоятельно рекомендуем **никогда** не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием `if` / `else`. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую. + diff --git a/external/JavaScript-Garden/doc/ru/core/undefined.md b/external/JavaScript-Garden/doc/ru/core/undefined.md new file mode 100644 index 0000000..092aec6 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/core/undefined.md @@ -0,0 +1,53 @@ +## `undefined` и `null` + +В JavaScript есть два отдельных типа для представления `ничего`, при этом более полезным из них является `undefined`. + +### Тип `undefined` + +`undefined` — это тип с единственным возможным значением: `undefined`. + +Кроме этого, в языке определена глобальная переменная со значением `undefined`, и эта переменная так и называется — `undefined`. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить. + +> **ES5 Замечание:** в ECMAScript 5 переменная `undefined` **больше не** *доступна на запись* в strict-режиме, однако она всё так же может быть перегружена по имени, например - функцией с именем `undefined`. + +Несколько случаев, когда возвращается `undefined`: + + - При попытке доступа к глобальной переменной `undefined` (если она не изменена). + - Неявный возврат из функции при отсутствии в ней оператора `return`. + - Из операторов `return`, которые ничего не возвращают. + - В результате поиска несуществующего свойства у объекта (и доступа к нему). + - Параметры, которые не были переданы в функцию явно. + - При доступе ко всему, чьим значением является `undefined`. + +### Обработка изменений значения `undefined` + +Поскольку глобальная переменная `undefined` содержит копию настоящего *значения* `undefined`, присвоение этой переменной нового значения **не** изменяет значения *типа* `undefined`. + +Но при этом, чтобы сравнить что-либо со *значением* `undefined`, прежде нужно получить значение самой *переменной* `undefined`. + +Чтобы защитить код от переопределения переменной `undefined`, часто используется техника [анонимной обёртки](#function.scopes), которая использует отсутствующий аргумент. + + var undefined = 123; + (function(something, foo, undefined) { + // в локальной области видимости `undefined` + // снова ссылается на правильное значене. + + })('Hello World', 42); + +Другой способ достичь того же эффекта — использовать определение внутри обёртки. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Единственная разница между этими вариантами в том, что последняя версия будет больше на 4 байта при минификации, а в первом случае внутри анонимной обёртки нет дополнительного оператора `var`. + +### Использование `null` + +Хотя `undefined` в контексте языка JavaScript чаще используется в качестве традиционного *null*, настоящий `null` (и тип и литерал) является в большей или меньшей степени просто другим типом данных. + +Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов за счёт присваивания `Foo.prototype = null`). Но в большинстве случаев тип `null` может быть заменён на `undefined`. + diff --git a/external/JavaScript-Garden/doc/ru/function/arguments.md b/external/JavaScript-Garden/doc/ru/function/arguments.md new file mode 100644 index 0000000..c96c76f --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/arguments.md @@ -0,0 +1,95 @@ +## Объект `arguments` + +В области видимости любой функции в JavaScript есть доступ к специальной переменной `arguments`. Эта переменная содержит в себе список всех аргументов, переданных данной функции. + +> **Замечание:** В случае, если переменная `arguments` уже была объявлена в области видимости функции либо путём присвоения через выражение `var`, либо являясь формальным параметром, объект `arguments` не будет создан. + +Объект `arguments` **не** является наследником `Array`. Он, конечно же, очень похож на массив и даже содержит свойство `length` — но он не наследует `Array.prototype`, а представляет собой `Object`. + +По этой причине, у объекта `arguments` **отсутствуют** стандартные методы массивов, такие как `push`, `pop` или `slice`. Хотя итерация с использованием обычного цикла `for` по аргументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа `Array`, чтобы применять к нему стандартные методы массивов. + +### Конвертация в массив + +Указанный код вернёт новый массив типа `Array`, содержащий все элементы объекта `arguments`. + + Array.prototype.slice.call(arguments); + +Эта конвертация занимает **много времени** и использовать её в критических частях кода **не рекомендуется**. + +### Передача аргументов + +Ниже представлен рекомендуемый способ передачи аргументов из одной функции в другую. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // делаем здесь что-нибудь + } + +Другой трюк — использовать и `call` и `apply` вместе, чтобы быстро создать несвязанную обёртку: + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Создаём несвязанную версию "method" + // Она принимает параметры: this, arg1, arg2...argN + Foo.method = function() { + + // Результат: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + + }; + + +### Формальные аргументы и индексы аргументов + +Объект `arguments` создаёт по *геттеру* и *сеттеру* и для всех своих свойств и для формальных параметров функции. + +В результате, изменение формального параметра также изменит значение соответствующего свойства объекта `arguments` и наоборот. + + 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); + +### Мифы и правда о производительности + +Объект `arguments` создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет. + +*Геттеры* и *сеттеры* создаются **всегда**; так что их использование практически никак не влияет на производительность. + +> **ES5 Замечание:** Эти *геттеры* и *сеттеры* не создаются в strict-режиме. + +Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование `arguments.callee`. + + function foo() { + arguments.callee; // сделать что-либо с этим объектом функции + arguments.callee.caller; // и с вызвавшим его объектом функции + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // При обычных условиях должна бы была быть развёрнута... + } + } + +В коде выше, функция `foo` не может [быть развёрнута][1] (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова. + +**Крайне не рекомендуется** использовать `arguments.callee` или какое-либо из его свойств. **Никогда**. + +> **ES5 Замечание:** В strict-режиме использование `arguments.callee` породит `TypeError`, поскольку его использование принято устаревшим. + +[1]: http://en.wikipedia.org/wiki/Inlining + diff --git a/external/JavaScript-Garden/doc/ru/function/closures.md b/external/JavaScript-Garden/doc/ru/function/closures.md new file mode 100644 index 0000000..b96ccf6 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/closures.md @@ -0,0 +1,80 @@ +## Замыкания и ссылки + +Одним из самых мощных инструментов JavaScript'а считаются возможность создавать *замыкания* — это такой приём, когда наша область видимости **всегда** имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это [функции](#function.scopes): т.о. объявляя функцию, вы автоматически реализуете замыкания. + +### Эмуляция приватных свойств + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +В данном примере `Counter` возвращает **два** замыкания: функции `increment` и `get`. Обе эти функции сохраняют **ссылку** на область видимости `Counter` и, соответственно, имеют доступ к переменной `count` из этой самой области. + +### Как это работает + +Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить `count` извне **не** представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +В приведенном примере мы **не** изменяем переменную `count` в области видимости `Counter`, т.к. `foo.hack` не объявлен в **данной** области. Вместо этого будет создана или перезаписана *глобальная* переменная `count`; + +### Замыкания внутри циклов + +Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Данный код **не** будет выводить числа с `0` до `9`, вместо этого число `10` будет выведено десять раз. + +*Анонимная* функция сохраняет **ссылку** на `i` и, когда будет вызвана функция `console.log`, цикл `for` уже закончит свою работу, а в `i` будет содержаться `10`. + +Для получения желаемого результата необходимо создать **копию** переменной `i`. + +### Во избежание ошибок + +Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать [анонимную функцию](#function.scopes) как обёртку. + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит `i`, **значение** которой будет скопировано в параметр `e`. + +Анонимная функция, которая передается в `setTimeout`, теперь содержит ссылку на `e`, значение которой **не** изменяется циклом. + +Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +> **Замечание** от перев. Переменную `e` можно тоже назвать `i`, если вы хотите: это не поменяет поведения кода — внутренняя переменная `i` всё так же будет *копией* внешней переменной + diff --git a/external/JavaScript-Garden/doc/ru/function/constructors.md b/external/JavaScript-Garden/doc/ru/function/constructors.md new file mode 100644 index 0000000..7a322af --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/constructors.md @@ -0,0 +1,101 @@ +## Конструктор + +Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова `new`, будет конструктором. + +Внутри конструктора (вызываемой функции) `this` будет указывать на новосозданный `Object`. [Прототипом](#object.prototype) этого **нового** объекта будет `prototype` функции, которая была вызвана в качестве конструктора. + +Если вызываемая функция не имеет явного возврата посредством `return`, то вернётся `this` — этот новый объект. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +В этом примере `Foo` вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к `Foo.prototype`. + +В случае, когда функция в явном виде возвращает некое значение используя `return`, то в результате выполнения конструктора мы получим именно его, **но только** если возвращаемое значение представляет собой `Object`. + + function Bar() { + return 2; + } + new Bar(); // новый объект + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // возвращённый объект + +Если же опустить ключевое слово `new`, то функция **не** будет возвращать никаких объектов. + + function Foo() { + this.bla = 1; // устанавливается глобальному объекту + } + Foo(); // undefined + +Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением [`this`](#function.this) в JavaScript — он будет восприниматься парсером как *глобальный объект*. + +### Фабрики + +Если хотите избавится от необходимости использования `new`, напишите конструктор, возвращающий значение посредством `return`. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +В обоих случаях при вызове `Bar` мы получим один и тот же результат — новый объект со свойством `method` (спасибо [замыканию](#function.closures) за это). + +Также следует заметить, что вызов `new Bar()` никак **не** связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но `Bar` никогда не возвращает этот новый объект. + +В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором `new` или без него. + +### Создание объектов с использованием фабрик + +Часто **не** рекомендуют использовать `new`, поскольку если вы его забудете, это может привести к ошибкам. + +Чтобы создать новый объект, лучше использовать фабрику и создать новый объект *внутри* этой фабрики. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +Хотя данный пример и сработает, если вы забыли ключевое слово `new`, и благодаря ему легче работать с [приватными переменными](#function.closures), у него есть несколько недостатков + + 1. Он использует больше памяти, поскольку созданные объекты **не** хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода. + 2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый. + 3. Разрыв цепочки прототипов просто по причине забытого ключевого слова `new` идёт вразрез с духом языка. + +### Заключение + +Хотя забытое ключевое слово `new` и может привести к багам, это точно **не** причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого **не изменять** ему. + diff --git a/external/JavaScript-Garden/doc/ru/function/general.md b/external/JavaScript-Garden/doc/ru/function/general.md new file mode 100644 index 0000000..eaf48c6 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/general.md @@ -0,0 +1,45 @@ +## Выражения и объявление функций + +Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача *анонимной функции* как функции обратного вызова в другую функцию — к примеру, для асинхронных вызовов. + +### Объявление `function` + + // всё просто и привычно + function foo() {} + +В следующем примере описанная функция [резервируется](#function.scopes) перед запуском всего скрипта; за счёт этого она доступна *в любом месте* кода, вне зависимости от того, где она *определена* — даже если функция вызывается до её фактического объявления в коде. + + + foo(); // сработает, т.к. функция будет создана до выполнения кода + function foo() {} + +### `function` как выражение + + var foo = function() {}; + +В этом примере безымянная и *анонимная* функция присваивается переменной `foo`. + + foo; // 'undefined' + foo(); // вызовет TypeError + var foo = function() {}; + +Так как в данном примере выражение `var` — это определение функции, переменная с именем `foo` будет заранее зарезервирована перед запуском скрипта (таким образом, `foo` уже будет определена во время его работы). + +Но поскольку присвоения исполняются непосредственно во время работы кода, `foo` по умолчанию будет присвоено значение [`undefined`](#core.undefined) (до обработки строки с определением функции): + + var foo; // переменная неявно резервируется + foo; // 'undefined' + foo(); // вызовет TypeError + foo = function() {}; + +### Выражения с именованными фунциями + +Существует еще нюанс, касающийся именованных функций создающихся через присваивание: + + var foo = function bar() { + bar(); // работает + } + bar(); // получим ReferenceError + +Здесь объект `bar` не доступен во внешней области, так как имя `bar` используется только для присвоения переменной `foo`; однако `bar` можно вызвать внутри функции. Такое поведение связано с особенностью работы JavaScript с [пространствами имен](#function.scopes) - имя функции *всегда* доступно в локальной области видимости самой функции. + diff --git a/external/JavaScript-Garden/doc/ru/function/scopes.md b/external/JavaScript-Garden/doc/ru/function/scopes.md new file mode 100644 index 0000000..0cf88ca --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/scopes.md @@ -0,0 +1,194 @@ +## Области видимости и пространства имён + +Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он **не** поддерживает блочную область видимости; всё что остаётся на этот случай в языке — *область видимости функций*. + + function test() { // область видимости + for(var i = 0; i < 10; i++) { // не область видимости + // считаем + } + console.log(i); // 10 + } + +> **Замечание:** Нотация `{...}` будет интерпретирована как блочное выражение, а **не** как литерал объекта, если она не используется в присваивании, операторе `return` или в качестве функции. Это замечание, вкупе с [автоматической расстановкой точек с запятой](#core.semicolon), может привести к чрезвычайно хитрым ошибкам. + +Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в *глобально доступном* пространстве имён. + +Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он ругнётся `ReferenceError`. + +### Проклятие глобальных переменных + + // скрипт A + foo = '42'; + + // скрипт B + var foo = '42' + +Вышеприведённые два скрипта **не** приводят к одному результату. Скрипт A определяет переменную по имени `foo` в *глобальной* области видимости, а скрипт B определяет `foo` в текущей области видимости. + +Повторимся, это вообще **не** *тот же самый эффект*. Если вы не используете `var` — то вы в большой опасности. + + // глобальная область видимости + var foo = 42; + function test() { + // локальная область видимости + foo = 21; + } + test(); + foo; // 21 + +Из-за того что оператор `var` опущен внутри функции, фунция `test` перезапишет значение `foo`. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете `var`, то вам на пути встретятся страшные и трудноотлаживаемые ошибки — и это не шутка. + + // глобальная область видимости + var items = [/* какой-то список */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // область видимости subLoop + for(i = 0; i < 10; i++) { // пропущенный оператор var + // делаем волшебные вещи! + } + } + +Внешний цикл прекратит работу сразу после первого вызова `subLoop`, поскольку `subLoop` перезаписывает глобальное значение переменной `i`. Использование `var` во втором цикле `for` могло бы вас легко избавить от этой ошибки. **Никогда** не забывайте использовать `var`, если только влияние на внешнюю область видимости не является тем, что вы *намерены получить*. + +### Локальные переменные + +Единственный источник локальных переменных в JavaScript - это параметры [функций](#function.general) и переменные, объявленные с использованием оператора `var`. + + // глобальная область видимости + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // локальная область видимости для функции test + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +В то время как `foo` и `i` — локальные переменные в области видимости функции `test`, присвоение `bar` переопределит значение одноимённой глобальной переменной. + +### Всплытие + +В JavaScript действует механизм **всплытия** определения. Это значит, что оба определения с использованием `var` и определение `function` будут перенесены наверх заключающей их области видимости. + + 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]; + } + } + +Этот код трансформируется ещё перед исполнением. JavaScript перемещает операторы `var` и определение `function` наверх ближайшей оборачивающей области видимости. + + // выражения с var переместились сюда + var bar, someValue; // по умолчанию - 'undefined' + + // определение функции тоже переместилось + function test(data) { + var goo, i, e; // потерянная блочная область видимости + // переместилась сюда + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // вылетает с ошибкой TypeError, + // поскольку bar всё ещё 'undefined' + someValue = 42; // присвоения не подвержены всплытию + bar = function() {}; + + test(); + +Потерянная область видимости блока не только переместит операторы `var` вовне циклов и их тел, но и сделает результаты некоторых конструкций с `if` неинтуитивными. + +В исходном коде оператор `if` изменял *глобальную переменную* `goo`, когда, как оказалось, он изменяет *локальную переменную* — в результате работы всплытия. + +Если вы не знакомы со *всплытием*, то можете посчитать, что нижеприведённый код должен породить +`ReferenceError`. + + // проверить, проинициализована ли SomeImportantThing + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Но, конечно же, этот код работает: из-за того, что оператор `var` был перемещён наверх *глобальной области видимости* + + var SomeImportantThing; + + // другой код может инициализировать здесь переменную SomeImportantThing, + // а может и нет + + // убедиться, что она всё ещё здесь + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### Порядок разрешения имён + +Все области видимости в JavaScript, включая *глобальную области видимости*, содержат специальную, определённую внутри них, переменную [`this`](#function.this), которая ссылается на *текущий объект*. + +Области видимости функций также содержат внутри себя переменную [`arguments`](#function.arguments), которая содержит аргументы, переданные в функцию. + +Например, когда JavaScript пытается получить доступ к переменной `foo` в области видимости функции, он будет искать её по имени в такой последовательности: + + 1. Если в текущей области видимости есть выражение `var foo`, использовать его. + 2. Если один из параметров функции называется `foo`, использовать его. + 3. Если функция сама называется `foo`, использовать её. + 4. Перейти на одну область видимости выше и начать с **п. 1** + +> **Замечание:** Наличие параметра функции с именем `arguments` **не позволит** движку создать объект `arguments`, создающийся по умолчанию. + +### Пространства имён + +Нередкое последствие наличия только одного глобального пространства имён — проблема с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя *анонимные обёртки*. + + (function() { + // самостоятельно созданное "пространство имён" + + window.foo = function() { + // открытое замыкание + }; + + })(); // сразу же выполнить функцию + +Безымянные функции являются [выражениями](#function.general); поэтому, чтобы вы имели возможность их выполнить, они сперва должны быть разобраны. + + ( // разобрать функцию внутри скобок + function() {} + ) // и вернуть объект функции + () // вызвать результат разбора + +Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, но действуют одинаково. + + // Два других способа + +function(){}(); + (function(){}()); + +### Заключение + +Рекомендуется всегда использовать *анонимную обёртку* для заключения кода в его собственное пространство имён. Это не только защищает код от совпадений имён, но и позволяет создавать более модульные программы. + +Важно добавить, что использование глобальных переменных считается **плохой практикой**. **Любое** их использование демонстрирует плохое качество кода и может привести к трудноуловимым ошибкам. + diff --git a/external/JavaScript-Garden/doc/ru/function/this.md b/external/JavaScript-Garden/doc/ru/function/this.md new file mode 100644 index 0000000..6883a54 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/function/this.md @@ -0,0 +1,92 @@ +## Как работает `this` + +В JavaScript область ответственности специальной переменной `this` концептуально отличается от того, за что отвечает `this` в других языках программирования. Различают ровно **пять** вариантов того, к чему привязывается `this` в языке. + +### 1. Глобальная область видимости + + this; + +Когда мы используем `this` в глобальной области, она будет просто ссылаться на *глобальный* объект. + +### 2. Вызов функции + + foo(); + +Тут `this` также ссылается на *глобальный* объект. + +> **ES5 Замечание:** В strict-режиме **теряется** понятие глобальности, поэтому в этом случае `this` будет иметь значение `undefined`. + +### 3. Вызов метода + + test.foo(); + +В данном примере `this` ссылается на `test`. + +### 4. Вызов конструктора + + new foo(); + +Если перед вызовом функции присутствует ключевое слово `new`, то данная функция будет действовать как [конструктор](#function.constructors). Внутри такой функции `this` будет указывать на *новосозданный* `Object`. + +### 5. Переопределение `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3 + foo.call(bar, 1, 2, 3); // аналогично + +Когда мы используем методы `call` или `apply` из `Function.prototype`, то внутри вызываемой функции `this` **явным образом** будет присвоено значение первого передаваемого параметра. + +Исходя из этого, в предыдущем примере (строка с `apply`) правило #3 *вызов метода* **не** будет применено, и `this` внутри `foo` будет присвоено `bar`. + +> **Замечание:** `this` **нельзя** использовать внутри литералов `{}` (`Object`) для ссылки на сам объект. Т.е. если мы напишем `var obj = {me: this}`, то `me` не будет ссылаться на `obj`, поскольку `this` присваивается только по одному из пяти описанных правил. + +### Наиболее распространенные ошибки + +Хотя большинство из примеров ниже наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он **вообще** не имеет практического применения. + + Foo.method = function() { + function test() { + // this ссылается на глобальный объект + } + test(); + }; + +Распространенным заблуждением будет то, что `this` внутри `test` ссылается на `Foo`, но это **не так**. + +Для того, чтобы получить доступ к `Foo` внутри функции `test`, необходимо создать локальную переменную внутри `method`, которая и будет ссылаться на `Foo`. + + Foo.method = function() { + var that = this; + function test() { + // Здесь используем that вместо this + } + test(); + }; + +Подходящее имя для переменной - `that`, его часто используют для ссылки на внешний `this`. В комбинации с [замыканиями](#function.closures) `this` можно пробрасывать в глобальную область или в любой другой объект. + +> **Замечание** от перев. Кроме `that` также часто встречаются `this_`, `self_` и другие варианты, но лучше принять для себя `that` как стандарт и тогда, возможно, все вокруг тоже будут им пользоваться. + +### Назначение методов + +Еще одной фичей, которая **не** работает в `JavaScript`, является создание псевдонимов для методов, т.е. **присвоение** метода объекта переменной. + + var test = someObject.methodTest; + test(); + +Следуя первому правилу `test` вызывается как обычная функция; следовательно `this` внутри него больше не ссылается на `someObject`. + +Хотя позднее связывание `this` на первый взгляд может показаться плохой идеей, но на самом деле именно благодаря этому работает [наследование прототипов](#object.prototype). + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +В момент, когда будет вызван `method` нового экземпляра `Bar`, `this` будет ссылаться на этот самый экземпляр. + diff --git a/external/JavaScript-Garden/doc/ru/index.json b/external/JavaScript-Garden/doc/ru/index.json new file mode 100644 index 0000000..9e27bf0 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/index.json @@ -0,0 +1,82 @@ +{ + "title": "JavaScript Гарден", + "langTitle": "JavaScript Гарден по-русски", + "description": "Руководство по хитростям и трюкам JavaScript.", + "sections": [ + { + "title": "Вступление", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "translators", + "license" + ] + }, + { + "title": "Объекты", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "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" + ] + }, + { + "title": "Нативности", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "Другое", + "dir": "other", + "articles": [ + "timeouts" + ] + }, + { + "title": "Пояснения", + "dir": "appendix", + "articles": [ + "fromtranslators" + ] + } + + ] +} + diff --git a/external/JavaScript-Garden/doc/ru/intro/authors.md b/external/JavaScript-Garden/doc/ru/intro/authors.md new file mode 100644 index 0000000..3c11a24 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/intro/authors.md @@ -0,0 +1,7 @@ +## Авторы + +Это руководство является результатом работы двух заядлых пользователей Stack Overflow: [Иво Ветцель /Ivo Wetzel/][1] (автора текста) и [Чжан И Цзян /Zhang Yi Jiang/][2] (дизайнера). + +[1]: http://stackoverflow.com/users/170224/ivo-wetzel +[2]: http://stackoverflow.com/users/313758/yi-jiang + diff --git a/external/JavaScript-Garden/doc/ru/intro/contributors.md b/external/JavaScript-Garden/doc/ru/intro/contributors.md new file mode 100644 index 0000000..159fb87 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/intro/contributors.md @@ -0,0 +1,4 @@ +## Участники + +- [Участники](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + diff --git a/external/JavaScript-Garden/doc/ru/intro/index.md b/external/JavaScript-Garden/doc/ru/intro/index.md new file mode 100644 index 0000000..9debe34 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/intro/index.md @@ -0,0 +1,8 @@ +## Вступление + +**JavaScript Гарден** — это постоянно обновляющаяся и растущая документация по самым заковыристым темам языка JavaScript. В ней вы найдёте советы о том, как избежать распространённых ошибок и предсказать появление тех или иных багов. В документации подробно освещены проблемы оптимизации и нерекомендуемые практики с которыми, продираясь к глубинам языка, могут столкнуться даже просвещённые JavaScript-программисты. + +JavaScript Гарден **не** имеет цели научить вас языку JavaScript. Вам понадобится реальный опыт работы с языком чтобы понимать темы, рассматриваемые в этом руководстве. Если вам требуется изучить основы языка, пожалуйста внимательно ознакомьтесь с замечательным [руководством][1] на сайте Mozilla Developer Network. + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/external/JavaScript-Garden/doc/ru/intro/license.md b/external/JavaScript-Garden/doc/ru/intro/license.md new file mode 100644 index 0000000..3383980 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/intro/license.md @@ -0,0 +1,9 @@ +## Лицензия + +JavaScript Гарден распространяется под [лицензией MIT][1] и располагается на [GitHub][2]. Если вы найдёте ошибку или опечатку, пожалуйста [сообщите нам о ней][3] или запросите права на загрузку в репозиторий. Кроме того, вы можете найти нас в [комнате JavaScript][4] среди чатов Stack Overflow. + +[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 + diff --git a/external/JavaScript-Garden/doc/ru/intro/translators.md b/external/JavaScript-Garden/doc/ru/intro/translators.md new file mode 100644 index 0000000..d08ba2a --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/intro/translators.md @@ -0,0 +1,12 @@ +## Переводчики + + - ['shaman.sir'][1] + - [Антон Шевчук][2] + - [Максим Лозовой][3] + - [Елена Пашкова][4] + +[1]: http://shamansir.madfire.net/ +[2]: http://anton.shevchuk.name/ +[3]: http://nixsolutions.com/ +[4]: http://nixsolutions.com/ + diff --git a/external/JavaScript-Garden/doc/ru/object/forinloop.md b/external/JavaScript-Garden/doc/ru/object/forinloop.md new file mode 100644 index 0000000..3bc5c8d --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/object/forinloop.md @@ -0,0 +1,37 @@ +## Цикл `for in` + +Как и оператор `in`, цикл `for in` проходит по всей цепочке прототипов, обходя свойства объекта. + +> **Примечание:** Цикл `for in` **не** обходит те свойства объекта, у которых атрибут `enumerable` установлен в `false`; как пример - свойство `length` у массивов + + // Испортим Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // печатает и bar и moo + } + +Так как изменить поведение цикла `for in` как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используют метод [`hasOwnProperty`](#object.hasownproperty) из `Object.prototype`. + +> **Примечание:** Цикл `for in` всегда обходит всю цепочку прототипов полностью: таким образом, чем больше прототипов (слоёв наследования) в цепочке, тем медленнее работает цикл. + +### Использование `hasOwnProperty` в качестве фильтра + + // возьмём foo из примера выше + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Это единственная версия правильного использования цикла. Благодаря использованию `hasOwnProperty` будет выведено **только** свойство `moo`. Если же убрать `hasOwnProperty`, код становится нестабилен и могут возникнуть ошибки, особенно если кто-то изменил встроенные прототипы, такие как `Object.prototype`. + +Один из самых популярных фреймворков [Prototype][1] как раз этим и славится, и если вы его подключаете, то не забудьте использовать `hasOwnProperty` внутри цикла `for in`, иначе у вас гарантированно возникнут проблемы. + +### Рекомендации + +Рекомендация одна — **всегда** используйте `hasOwnProperty`. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/ru/object/general.md b/external/JavaScript-Garden/doc/ru/object/general.md new file mode 100644 index 0000000..aa5ad53 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/object/general.md @@ -0,0 +1,99 @@ +## Объекты и их свойства + +В JavaScript всё ведет себя, как объект, лишь за двумя исключениями — [`null`](#core.undefined) и [`undefined`](#core.undefined). + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение *точечной нотации* к числу воспринимается им как литерал числа с плавающей точкой. + + 2.toString(); // вызывает SyntaxError + +Есть несколько способов обойти этот недостаток и любой из них можно использовать для того, чтобы работать с числами, как с объектами: + + 2..toString(); // вторая точка распознаётся корректно + 2 .toString(); // обратите внимание на пробел перед точкой + (2).toString(); // двойка вычисляется заранее + +### Объекты как тип данных + +Объекты в JavaScript могут использоваться как [*хеш-таблицы*][1]: подавляющей частью состоят из именованных свойств (ключей), привязанных к значениям. + +Используя объектный литерал — нотацию `{}` — можно создать простой объект. Новый объект [наследуется](#object.prototype) от `Object.prototype` и не имеет [собственных свойств](#object.hasownproperty). + + var foo = {}; // новый пустой объект + + // новый объект со свойством 'test', имеющим значение 12 + var bar = {test: 12}; + +### Доступ к свойствам + +Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись квадратными скобками. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // работает + +Обе нотации идентичны по принципу работы — одна лишь разница в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке. + +### Удаление свойств + +Единственный способ удалить свойство у объекта — использовать оператор `delete`; устанавливая свойство в `undefined` или `null`, вы только заменяете связанное с ним *значение*, но не удаляете *ключ*. + +> **Замечание** от перев.: Если ссылок на значение больше нет, то сборщиком мусора удаляется и само значение, но ключ объекта при этом всё так же имеет новое значение. + + 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]); + } + } + +Приведённый код выведет две строки: `bar undefined` и `foo null` — на самом деле удалено было только свойство `baz` и посему только оно будет отсутствовать в выводе. + +### Запись ключей + + var test = { + 'case': 'Я — ключевое слово, поэтому меня надо записывать строкой', + delete: 'Я тоже ключевое слово, так что я' // не является ошибкой, бросает SyntaxError только в версиях ECMAScript ниже 5ой версии + }; + +Свойства объектов могут записываться как явно символами, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит `SyntaxError` во всех версиях ранее ECMAScript 5. + +Источником ошибки является факт, что `delete` — это *ключевое слово* и поэтому его необходимо записывать как *строчный литерал*: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript. + +*От перев.:* И еще один пример в пользу строковой нотации, это относится к [JSON][2]: + + // валидный JavaScript и валидный JSON + { + "foo": "oof", + "bar": "rab" + } + + // валидный JavaScript и НЕвалидный JSON + { + foo: "oof", + bar: "rab" + } + +[1]: http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 +[2]: http://ru.wikipedia.org/wiki/JSON + diff --git a/external/JavaScript-Garden/doc/ru/object/hasownproperty.md b/external/JavaScript-Garden/doc/ru/object/hasownproperty.md new file mode 100644 index 0000000..89b950c --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/object/hasownproperty.md @@ -0,0 +1,40 @@ +## Функция `hasOwnProperty` + +Если вам необходимо проверить, определено ли свойство у *самого объекта*, а **не** в его [цепочке прототипов](#object.prototype), вы можете использовать метод `hasOwnProperty`, который все объекты наследуют от `Object.prototype`. + +> **Примечание:** Для проверки наличия свойства **недостаточно** проверять, эквивалентно ли оно `undefined`. Свойство может вполне себе существовать, но при этом ему может быть присвоено значение `undefined`. + +`hasOwnProperty` — единственная функция в JavaScript, которая позволяет получить свойства объекта **без обращения** к цепочке его прототипов. + + // испортим 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 + +Только используя `hasOwnProperty` можно гарантировать правильный результат при переборе свойств объекта. И **нет** иного способа для определения свойств, которые определены в *самом* объекте, а не где-то в цепочке его прототипов. + +### `hasOwnProperty` как свойство + +JavaScript **не** резервирует свойство с именем `hasOwnProperty`. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем, требуется использовать *внешний* вариант функции `hasOwnProperty` чтобы получить корректные результаты. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Да прилетят драконы' + }; + + foo.hasOwnProperty('bar'); // всегда возвращает false + + // Используем метод hasOwnProperty пустого объекта + // и передаём foo в качестве this + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### Заключение + +**Единственным** способом проверить существование свойства у объекта является использование метода `hasOwnProperty`. При этом рекомендуется использовать этот метод в **каждом** [цикле `for in`](#object.forinloop) вашего проекта, чтобы избежать возможных ошибок с ошибочным заимствованием свойств из [прототипов](#object.prototype) родительских объектов. Также вы можете использовать конструкцию `{}.hasOwnProperty.call(...)` на случай, если кто-то вздумает расширить [прототипы](#object.prototype) встроенных объектов. diff --git a/external/JavaScript-Garden/doc/ru/object/prototype.md b/external/JavaScript-Garden/doc/ru/object/prototype.md new file mode 100644 index 0000000..a8e7512 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/object/prototype.md @@ -0,0 +1,87 @@ +## Великий Прототип + +В JavaScript отсутствует классическая модель наследования — вместо неё используется [*прототипная модель*][1]. + +Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть. + +Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями. + +Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых *цепочек прототипов*. + +> **Замечание:** В результате выполнения конструкции `Bar.prototype = Foo.prototype` оба объекта будут делить друг с другом **один и тот же** прототип. Так что изменение прототипа одного из объектов повлечёт за собой изменение прототипа другого и наоборот — вряд ли это окажется тем, чего вы ожидали. + +> **Замечание:** Для объявления наследования вместо `Bar.prototype = Object.create(Foo.prototype)` можно воспользоваться конструкций `Bar.prototype = new Foo()`, но у нее есть пару недостатков: 1) как правило требуется унаследовать только методы и свойства прототипа, а не создавать для этого новый объект; 2) создание объекта может требовать обязательные аргументы. + +> **Примечание:** Метод `Object.create` отсутствует в IE8 и ниже, но его легко реализовать созданием своей такой функции или же можно подключить библиотеку для поддержки старых IE [*es5-shim*][5] + + function Foo() { + this.value = 42; + } + Foo.prototype.method = function() {} + + function Bar() {} + + // Зададим наследование от Foo + Bar.prototype = Object.create(Foo.prototype); + Bar.prototype.foo = 'Hello World'; + + // Убедимся, что Bar является действующим конструктором + Bar.prototype.constructor = Bar; + + var test = new Bar() // создадим новый экземпляр bar + + // Цепочка прототипов, которая получится в результате + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* и т.д. */ } + +В приведённом коде объект `test` наследует оба прототипа: `Bar.prototype` и `Foo.prototype`; следовательно, он имеет доступ к функции `method` которую мы определили в прототипе `Foo`. Также у него есть доступ к свойству `value` **одного уникального** экземпляра `Foo`, который является его прототипом. Важно заметить, что код `new Bar()` **не** создаёт новый экземпляр `Foo`, а повторно вызывает функцию, которая была назначена его прототипом: таким образом все новые экземпляры `Bar` будут иметь **одинаковое** свойство `value`. + +> **Замечание:** Никогда **не** используйте конструкцию `Bar.prototype = Foo`, поскольку ссылка будет указывать не на прототип `Foo`, а на объект функции `Foo`. Из-за этого цепочка прототипов будет проходить через `Function.prototype`, а не через `Foo.prototype` и в результате функция `method` не будет содержаться в цепочке прототипов. + +### Поиск свойств + +При обращении к какому-либо свойству объекта, JavaScript проходит **вверх** по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем. + +Если он достигнет верхушки этой цепочки (`Object.prototype`) и при этом так и не найдёт указанное свойство, вместо него вернётся значение [undefined](#core.undefined). + +### Свойство `prototype` + +То, что свойство `prototype` используется языком для построения цепочек прототипов, даёт нам возможность присвоить **любое** значение этому свойству. Однако обычные примитивы, если назначать их в качестве прототипа, будут просто-напросто игнорироваться. + + function Foo() {} + Foo.prototype = 1; // ничего не произойдёт + Foo.prototype = { + "foo":"bar" + }; + +При этом присвоение объектов, как в примере выше, позволит вам динамически создавать цепочки прототипов. + +### Производительность + +Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями. + +Вдобавок, при [циклическом переборе](#object.forinloop) свойств объекта, будет обработано **каждое** свойство, существующее в цепочке прототипов. + +### Расширение встроенных прототипов + +Часто встречается неверное применение прототипов — расширение прототипа `Object.prototype` или прототипов одного из встроенных объектов JavaScript. + +Подобная практика нарушает принцип *инкапсуляции* и имеет соответствующее название — [monkey patching][2]. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют. + +**Единственным** оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функции [`Array.forEach`][4], которая появилась в версии 1.6. + +### Заключение + +Перед тем, как вы приступите к разработке сложных приложений на JavaScript, вы **должны** полностью осознать как работают прототипы, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того — **никогда** не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript). + +[1]: http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D1%82%D0%BE%D1%82%D0%B8%D0%BF%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5 +[2]: http://en.wikipedia.org/wiki/Monkey_patch +[3]: http://prototypejs.org/ +[4]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach +[5]: https://github.com/es-shims/es5-shim + diff --git a/external/JavaScript-Garden/doc/ru/other/timeouts.md b/external/JavaScript-Garden/doc/ru/other/timeouts.md new file mode 100644 index 0000000..d5eb03b --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/other/timeouts.md @@ -0,0 +1,115 @@ +### `setTimeout` и `setInterval` + +Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции `setTimeout` и `setInterval`. + +> **Замечание:** Таймауты **не** являются частью стандарта ECMAScript, они были разработаны как раздел спецификации [DOM][1]. + + function foo() {} + var id = setTimeout(foo, 1000); // возвращает число > 0 + +Функция `setTimeout` возвращает идентификатор таймаута и планирует вызвать `foo` через, **примерно**, тысячу миллисекунд. Функция `foo` при этом будет вызвана ровно **один** раз. + +В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет **никакой** гарантии, что переданный код будет выполнен ровно через указанное в вызове `setTimeout` время. + +Переданная первым параметром функция будет вызвана как *глобальный объект* — это значит, что оператор [`this`](#function.this) в вызываемой функции будет ссылаться на этот самый объект. + + function Foo() { + this.value = 42; + this.method = function() { + // this ссылается на глобальный объект + console.log(this.value); // выведет в лог undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Замечание:** Поскольку `setTimeout` принимает **объект функции** в качестве первого параметра, часто совершается ошибка в использовании `setTimeout(foo(), 1000)`, при котором будет использоваться **возвращённое значение** от вызова функции `foo`, а **не** вызываться сама функция `foo`. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает `undefined`, `setTimeout` вообще **не** породит никакой ошибки. + +### Поочерёдные вызовы с использованием `setInterval` + +`setTimeout` вызывает функцию единожды; `setInterval` — как и предполагает название — вызывает функцию **каждые** `X` миллисекунд. И его использование не рекомендуется. + +В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, `setInterval` будет продолжать планировать последующие вызовы переданной функции. Это может (особенно в случае небольших интервалов) повлечь за собой выстраивание вызовов функций в очередь. + + function foo(){ + // что-то, что выполняется одну секунду + } + setInterval(foo, 100); + +В приведённом коде `foo` выполнится один раз и заблокирует этим главный поток на одну секунду. + +Пока `foo` блокирует код, `setInterval` продолжает планировать последующие её вызовы. Теперь, когда первая `foo` закончила выполнение, в очереди будут уже **десять** ожидающих выполнения вызовов `foo`. + +### Разбираемся с потенциальной блокировкой кода + +Самый простой и контролируемый способ — использовать `setTimeout` внутри самой функции. + + function foo(){ + // что-то, выполняющееся одну секунду + setTimeout(foo, 100); + } + foo(); + +Такой способ не только инкапсулирует вызов `setTimeout`, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция `foo` теперь принимает решение, хочет ли она запускаться ещё раз или нет. + +### Очистка таймаутов вручную + +Удаление таймаутов и интервалов работает через передачу соответствующего идентификатора либо в функцию `clearTimeout`, либо в функцию `clearInterval` — в зависимости от того, какая функция `set...` использовалась для его получения. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Очистка всех таймаутов + +Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс. + + // удаляем "все" таймауты + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, за счёт чего их можно будет удалять индивидуально. + +### Скрытое использование `eval` + +`setTimeout` и `setInterval` могут принимать строку в качестве первого параметра. Эту возможность не следует использовать **никогда**, поскольку изнутри при этом производится скрытый вызов `eval`. + +> **Замечание**: Поскольку функции работы с таймаутами **не** определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известно, что Microsoft JScript использует конструктор `Function` вместо `eval`. + + function foo() { + // будет вызвана + } + + function bar() { + function foo() { + // никогда не будет вызывана + } + setTimeout('foo()', 1000); + } + bar(); + +Поскольку `eval` в этом случае не вызывается [напрямую](#core.eval), переданная в `setTimeout` строка будет выполнена в *глобальной области видимости*; так что локальная переменная `foo` из области видимости `bar` не будет выполнена. + +По этим же причинам рекомендуется **не** использовать строку для передачи аргументов в функцию, которая должна быть вызвана из одной из двух функций, работающих с таймаутами. + + function foo(a, b, c) {} + + // НИКОГДА не делайте такого + setTimeout('foo(1,2, 3)', 1000) + + // Вместо этого используйте анонимную функцию + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Замечание:** При том, что синтаксис `setTimeout(foo, 1000, 1, 2, 3)` разрешено использовать, это крайне не рекомендуется, поскольку может привести к сложно распознаваемым ошибкам при работе с [методами](#function.this). + +### Заключение + +**Никогда** не используйте строки как параметры `setTimeout` или `setInterval`. Это явный признак **действительно** плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать *анонимную функцию*, которая самостоятельно будет отвечать за сам вызов. + +Кроме того, избегайте использования `setInterval` в случаях, когда его планировщик может блокировать выполнение JavaScript. + +[1]: http://ru.wikipedia.org/wiki/Document_Object_Model + diff --git a/external/JavaScript-Garden/doc/ru/types/casting.md b/external/JavaScript-Garden/doc/ru/types/casting.md new file mode 100644 index 0000000..8e77c79 --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/types/casting.md @@ -0,0 +1,61 @@ +## Приведение типов + +JavaScript — *слабо типизированный* язык, поэтому *преобразование типов* будет применяться **везде**, где возможно. + + // Эти равенства — истинны + new Number(10) == 10; // объект типа Number преобразуется + // в числовой примитив в результате неявного вызова + // метода Number.prototype.valueOf + + 10 == '10'; // Strings преобразуется в Number + 10 == '+10 '; // Ещё чуток строко-безумия + 10 == '010'; // и ещё + isNaN(null) == false; // null преобразуется в 0, + // который конечно же не NaN + + // Эти равенства — ложь + 10 == 010; + 10 == '-10'; + +> **ES5 Замечание:** Числовые литералы, которые начинаются с 0, интерпретируются как восьмеричные (Base 8). В ECMAScript 5 strict mode **удалена** поддержка восьмеричной системы. + +Для того, чтобы избежать этого, **настоятельно** рекомендуется использовать [оператор строгого равенства](#types.equality). Впрочем, хотя это и позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript. + +### Конструкторы встроенных типов + +Конструкторы встроенных типов, например, `Number` и `String` ведут себя различным образом, в зависимости от того, вызываются они с ключевым словом `new` или без. + + new Number(10) === 10; // False, Object и Number + Number(10) === 10; // True, Number и Number + new Number(10) + 0 === 10; // True, из-за неявного преобразования + +Использование встроенного типа, такого как `Number`, в качестве конструктора создаёт новый экземпляр объекта Number, но при использовании без ключевого слова `new` функция `Number` будет вести себя как конвертер. + +Кроме того, присутствие литералов или переменных, которые не являются объектами, приведет к еще большему насилию над типами. + +Лучший вариант — это **явное** приведение к одному из трех возможных типов. + +### Приведение к строке + + '' + 10 === '10'; // true + +Путём добавления в начале пустой строки, значение легко приводится к строке. + +### Приведение к числовому типу + + +'10' === 10; // true + +Используя **унарный** оператор плюс, можно преобразовать значение в число. + +### Приведение к булеву типу + +Используя оператор **not** (**`!`**) дважды, значение может быть приведено к логическому (булеву) типу. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + diff --git a/external/JavaScript-Garden/doc/ru/types/equality.md b/external/JavaScript-Garden/doc/ru/types/equality.md new file mode 100644 index 0000000..a7255ca --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/types/equality.md @@ -0,0 +1,58 @@ +##  Равенство и сравнение + +JavaScript имеет 2 различных способа сравнения значений объектов на равенство. + +### Оператор сравнения + +Оператор сравнения состоит из **двух** символов равенства: `==` + +*Слабая типизированность* языка JavaScript подразумевает **приведение** обеих переменных к **одному типу** для того, чтобы произвести сравнение. + + "" == "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 + +В таблице выше показаны результаты приведения типов и это главная причина, почему использование `==` повсеместно считается плохой практикой: оно приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов. + +Кроме того, приведение типов во время сравнения также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом. + +### Оператор строгого равенства + +Оператор строгого равенства состоит из **трёх** символов равенства: `===` + +В отличие от обычного оператора равенства, оператор строгого равенства **не** выполняет приведение типов между операндами. + + "" === "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 + +Результаты выше более понятны и позволяют быстрее выявлять ошибки в коде. Это в определённой степени улучшает код, а также дает прирост производительности в случае, если операнды имеют различные типы. + +### Сравнение объектов + +Хотя оба оператора `==` и `===` заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы один из операндов является `Object`. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Здесь оба операнда сравниваются на **идентичность**, а **не** на равенство; то есть будет проверяться, являются ли операнды одним **экземпляром** объекта, так же как делает `is` в Python и сравниваются указатели в С. + +### Заключение + +Крайне рекомендуется использовать только операторы **строгого равенства**. В случае, когда намечается преобразование типов, нужно сделать [явное приведение](#types.casting) и не оставлять их на совести языковых хитростей с преобразованиями. + diff --git a/external/JavaScript-Garden/doc/ru/types/instanceof.md b/external/JavaScript-Garden/doc/ru/types/instanceof.md new file mode 100644 index 0000000..a19f2ce --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/types/instanceof.md @@ -0,0 +1,32 @@ +## Оператор `instanceof` + +Оператор `instanceof` сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как и [оператор typeof](#types.typeof). + +### Сравнение пользовательских объектов + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Всего лишь присваиваем Bar.prototype объект функции Foo, + // но не экземпляра Foo + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Использование `instanceof` со встроенными типами + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +Здесь надо отметить одну важную вещь: `instanceof` не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы и правда не будут конструкторами *тех самых* объектов. + +### Заключение + +Оператор `instanceof` должен использоваться **только** при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора `typeof`, любого другого использования необходимо **избегать**. + diff --git a/external/JavaScript-Garden/doc/ru/types/typeof.md b/external/JavaScript-Garden/doc/ru/types/typeof.md new file mode 100644 index 0000000..f19b62a --- /dev/null +++ b/external/JavaScript-Garden/doc/ru/types/typeof.md @@ -0,0 +1,64 @@ +## Оператор `typeof` + +Оператор `typeof` (вместе с [`instanceof`](#types.instanceof)) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он **поломан более, чем полностью**. + +Хотя `instanceof` еще имеет ограниченное применение, `typeof` на самом деле имеет *только один* практический случай применения, который при всём при этом **не** является проверкой типа объекта. + +> **Замечание:** Хотя для вызова `typeof` также можно использовать синтаксис функции, т.е. `typeof(obj)`, на самом деле это не функция. Двойные круглые скобки будут работать нормально и возвращаемое значение будет использоваться как операнд оператора `typeof`. Но функции `typeof` — **не существует**. + +### Таблица типов JavaScript + + Значение Класс Тип + ------------------------------------- + "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 в Nitro/V8) + new RegExp("meow") RegExp object (function в Nitro/V8) + {} Object object + new Object() Object object + +В таблице выше *Тип* представляет собой значение, возвращаемое оператором `typeof`. Как хорошо видно, это значение может быть абсолютно любым, но не логичным результатом. + +*Класс* представляет собой значение внутреннего свойства `[[Class]]` объекта. + +> **Из спецификации:** Значением `[[Class]]` может быть одна из следующих строк: `Arguments`, `Array`, `Boolean`, `Date`, `Error`, `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +Для того, чтобы получить значение `[[Class]]`, необходимо вызвать метод `toString` у `Object.prototype`. + +### Класс объекта + +Спецификация предоставляет только один способ доступа к значению `[[Class]]` — используя `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 + +В примере выше `Object.prototype.toString` вызывается со значением [this](#function.this), являющимся объектом, значение `[[Class]]` которого нужно получить. + +> **ES5 Замечание:** Для удобства в ECMAScript 5 возвращаемое значение `Object.prototype.toString `для `null` и `undefined` было изменено с `Object` на `Null` и `Undefined` соответственно. + +### Проверка переменных на определённость + + typeof foo !== 'undefined' + +Выше проверяется, было ли `foo` действительно объявлено или нет; просто обращение к переменной приведёт к `ReferenceError`. Это единственное, чем на самом деле полезен `typeof`. + +### Заключение + +Для проверки типа объекта настоятельно рекомендуется использовать` Object.prototype.toString` — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые `typeof` значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях. + +Кроме случая проверки, была ли определена переменная, `typeof` следует избегать **во что бы то ни стало**. + diff --git a/external/JavaScript-Garden/doc/tr/array/constructor.md b/external/JavaScript-Garden/doc/tr/array/constructor.md new file mode 100644 index 0000000..8e16990 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/array/constructor.md @@ -0,0 +1,35 @@ +## `Array` Oluşturucusu + +`Array` oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu +için, yeni diziler oluşturulurken her zaman dizi sabitlerinin (`[]` +notasyonu) kullanılması tavsiye olunur. + + [1, 2, 3]; // Sonuç: [1, 2, 3] + new Array(1, 2, 3); // Sonuç: [1, 2, 3] + + [3]; // Sonuç: [3] + new Array(3); // Sonuç: [] + new Array('3') // Sonuç: ['3'] + +`Array` oluşturucusuna tek bir argüman verildiğinde, ve bu argümanın türü +`Number` ise, oluşacak *boş* dizinin `length` özelliği argümanın +değerine eşit olacaktır. Bu şekilde oluşturulan bir dizinin **sadece** +`length` özelliği belirlenmiş olup dizi indisleri tanımsız olacaktır. + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, indisler atanmadı + +Dizinin uzunluğunu bu şekilde önceden belirlemek sadece bir iki durumda +kullanışlıdır. Bunlardan birisi bir döngüye gerek olmadan bir karakter +katarını tekrarlamaktır. + + new Array(count + 1).join(stringToRepeat); + +### Sonuç + +`Array` oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. +Bunun yerine her zaman dizi sabitleri tercih edilmelidir. Hem daha kısadırlar +hem de daha anlaşılır bir sentaksa sahiptirler; bu nedenle programın +okunabilirliğini de artırırlar. + diff --git a/external/JavaScript-Garden/doc/tr/array/general.md b/external/JavaScript-Garden/doc/tr/array/general.md new file mode 100644 index 0000000..6925a41 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/array/general.md @@ -0,0 +1,58 @@ +## Dizi İterasyonu ve Özellikleri + +Diziler JavaScript nesneleri olmalarına rağmen, iterasyon yapmak için +[`for in`](#object.forinloop) döngüsü kullanmak için bir neden yoktur. +Aslında dizilerde `for in` kullanılmasına **karşı** bazı iyi nedenler +vardır. + +> **Not:** JavaScript dizileri *associative* **değildir**. JavaScript ile sadece +> [nesneler](#object.general) ile anahtar-değer ilişkilendirmesi mümkündür. +> Ve *associative* diziler eleman sıralamasını **korurlar** ama, nesneler +> **korumazlar**. + +`for in` döngüsü prototip zincirindeki tüm özellikleri dolaştığı için ve bunu +engellemenin tek yolu [`hasOwnProperty`](#object.hasownproperty) kullanmak +olduğu için `for in` döngüsü sıradan bir `for` döngüsünden **yirmi kata kadar** +daha yavaştır. + +### İterasyon + +Dizilerde iterasyon yaparken en iyi performansı elde etmenin en iyi yolu klasik +`for` döngüsünü kullanmaktır. + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +Yukarıdaki örnekte bir optimizasyon var, o da dizinin uzunluğun iterasyonun +başında `l = list.length` ile saklanmış olması. + +`length` özelliği dizinin kendisinde tariflenmiş olmasına rağmen, her adımda +bu özelliği okumanın yine de bir maliyeti vardır. Modern JavaScript motorları +bu tür durumlar için **muhtemelen** optimizasyon yapıyor olsa bile, programın +her zaman modern bir motorda çalışacağından emin olmak mümkün değildir. + +Aslında, yukarıdaki örnekteki optimizasyonu uygulamamak döngünün +**iki kat daha** yavaş çalışmasına neden olabilir. + +### `length` özelliği + +`length` özelliğine değer atanarak diziyi **kısaltmak** için kullanılabilir. + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +Daha küçük bir uzunluk atanması diziyi kısaltır, fakat daha büyük bir uzunluk +atanmasının dizi üzerinde bir etkisi yoktur. + +### Sonuç + +En iyi performans için her zaman sıradan `for` döngüsü kullanılmalı ve +`length` özelliği saklanmalıdır. Dizilerde `for in` döngüsünün kullanılmış +olması hatalara meyilli kötü yazılmış bir programa işaret eder. + diff --git a/external/JavaScript-Garden/doc/tr/core/delete.md b/external/JavaScript-Garden/doc/tr/core/delete.md new file mode 100644 index 0000000..de19f1a --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/core/delete.md @@ -0,0 +1,84 @@ +## `delete` Operatörü + +Kısacası, genel kapsamda tanımlanmış değişkenleri, fonksiyonları ve `DontDelete` +niteliğine sahip bazı başka şeyleri silmek *imkansızdır*. + +### Genel kapsam ve fonksiyon kapsamı + +Bir değişken veya fonksiyon genel kapsamda veya +[fonksiyon kapsamında](#function.scopes) tanımlandığında aktivasyon nesnesinin +veya global nesnenin bir özelliği olacaktır. Bu tür özelliklerin bir takım +nitelikleri vardır ve bunlardan biri `DontDelete` niteliğidir. Genel kapsamda ve +fonksiyon kapsamında tanımlanan değişkenler ve fonksiyonlar yaratıldıklarında +her zaman `DontDelete` niteliğine sahip olacaktır, ve bu nedenle silinemezler. + + // genel kapsam değişkeni: + var a = 1; // DontDelete niteliğine sahip + delete a; // false + a; // 1 + + // normal bir fonksiyon: + function f() {} // DontDelete niteliğine sahip + delete f; // false + typeof f; // "function" + + // başka bir değişkene atamak işe yaramaz: + f = 1; + delete f; // false + f; // 1 + +### Açıkça tanımlanan özellikler + +Açıkça tanımlanan özellikleri silmek mümkündür. + + // tanımlanan özellik: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +Yukarıdaki örnekte `obj.x` ve `obj.y` silinebilir çünkü `DontDelete` niteliğine +sahip değillerdir. Aynı nedenle aşağıdakini yapmak da mümkündür: + + // IE hariç çalışır: + var GLOBAL_OBJECT = this; + GLOBAL_OBJECT.a = 1; + a === GLOBAL_OBJECT.a; // true - genel değişken + delete GLOBAL_OBJECT.a; // true + GLOBAL_OBJECT.a; // undefined + +Burada `a`'yı silmek için bir hile kullanıyoruz. [`this`](#function.this) +burada genel nesneye işaret ediyor ve `a` değişkenini onun özelliği olarak +atıyoruz, ve böylece onu silebiliyoruz. + +IE (en azından 6-8) bazı hatalar içerdiğinden yukarıdaki örnek çalışmayacaktır. + +### Fonksiyon argümanları ve önceden tanımlı özellikler + +Fonksiyonlara verilen argümanlar, [`arguments` nesnesi](#function.arguments) +ve önceden tanımlı özellikler de `DontDelete` niteliğine sahiptir. + + // fonksiyon argümanları ve özellikler: + (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* nesneler + +`Host` nesneler üzerinde kullanıldığında `delete` operatörünün davranışı belirsiz +olabilir. Standarda göre `host` nesneler istedikleri davranışı uygulayabilirler. + +### Sonuç + +`delete` operatörünün davranışı genellikle belirsizdir ve güvenle kullanılabileceği +tek yer sıradanan nesneler üzerinde açıkça tanımlanan özelliklerdir. diff --git a/external/JavaScript-Garden/doc/tr/core/eval.md b/external/JavaScript-Garden/doc/tr/core/eval.md new file mode 100644 index 0000000..1fa6416 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/core/eval.md @@ -0,0 +1,48 @@ +## Neden `eval` Kullanılmamalı + +`eval` fonksiyonu bir JavaScript kodunu lokal kapsamda yürütür. + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +Fakat `eval` sadece **direkt olarak** çağrıldığında *ve* çağrılan fonksiyonun +adı `eval` ise lokal kapsamda çalışır. + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +`eval` fonksiyonu **asla** kullanılmamalıdır. Kullanıldığı durumların %99.9'unda +`eval` **kullanılmadan** da istenen sonuç elde edilebilir. + +### Gizli `eval` + +[Zamanlama fonksiyonları](#other.timeouts) `setTimeout` ve `setInterval`'ın her +ikisinin de ilk argümanları bir karakter katarıdır. Bu durumda `eval` dolaylı +olarak çağrıldığı için bu argüman **her zaman** genel kapsamda yürütülecektir. + +### Güvenlik sorunları + +`eval` kendisine verilen **her** kodu işlettiği için aynı zamanda bir güvenlik +sorunudur ve **asla** kaynağı bilinmeyen yada güvenilir olmayan karakter +katarları ile kullanılmamalıdır. + +### Sonuç + +`eval` asla kullanılmamalıdır, kullanan programlar ise doğruluk, performans ve +güvenlik açılarından sorgulanmalıdır. `eval` kullanımı gerekli görülmüşse, +programın tasarımı sorgulanmalı ve **kullanılmamalı**, bunun yerine `eval` +gerektirmeyen *daha iyi bir tasarım* kullanılmalıdır. + diff --git a/external/JavaScript-Garden/doc/tr/core/semicolon.md b/external/JavaScript-Garden/doc/tr/core/semicolon.md new file mode 100644 index 0000000..89f5e3a --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/core/semicolon.md @@ -0,0 +1,118 @@ +## Otomatik Noktalı Virgül İlavesi + +JavaScript sentaksı C'ye benzese de, noktalı virgül kullanılması +zorunlu **değildir**. + +Fakat JavaScript noktalı virgül kullanmayan bir dil değildir, hatta +programı anlayabilmek için noktalı virgüllere ihtiyaç duyar. Bu yüzden +JavaScript gramer çözümleyicisi eksik bir noktalı virgül yüzünden bir +hata ile karşılaştığında **otomatik olarak** eksik noktalı virgülleri +ekler. + + var foo = function() { + } // hata, noktalı virgül gerekiyor + test() + +Eklemeden sonra çözümleme tekrarlanır. + + var foo = function() { + }; // hata ortadan kalktı, çözümleme devam edebilir + test() + +Noktalı virgüllerin bu şekilde otomatik olarak eklenmesi JavaScript'in +**en büyük** tasarım hatalarından biri olarak kabul edilir, çünkü programın +davranışını değiştirmesi *mümkündür*. + +### Ekleme nasıl olur + +Aşağıdaki örnekte hiç noktalı virgül yok, bu yüzden nereye noktalı virgül +eklenmesi gerektiğini gramer çözümleyicinin karar vermesi gerekiyor. + + (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) + +Çözümleyicinin "tahmin" oyununun sonucu aşağıdadır. + + (function(window, undefined) { + function test(options) { + + // Eklenmedi, satırlar birleştirildi + log('testing!')(options.list || []).forEach(function(i) { + + }); // <- eklendi + + options.value.test( + 'long string to pass here', + 'and another long string to pass' + ); // <- eklendi + + return; // <- eklendi, return ifadesi bozuldu + { // bir blok olarak değerlendirildi + + // bir yer etiketi ve bir ifade + foo: function() {} + }; // <- eklendi + } + window.test = test; // <- eklendi + + // Burada da satırlar birleştirildi + })(window)(function(window) { + window.someLibrary = {}; // <- eklendi + + })(window); //<- eklendi + +> **Not:** JavaScript çözümleyicisi `return` ifadesinden hemen sonra satır sonu +> gelmesi durumunu "doğru" değerlendirmez. Bu durum otomatik noktalı virgül +> eklenmesinin istenmeyen bir yan etkisidir. + +Çözümleyici yukarıdaki program parçasının davranışını büyük ölçüde değiştirdi, +belirli durumlarda da grameri değerlendirirken **yanlış** kararlar verdi. + +### Satır başındaki parantezler + +Bir satırın parantez ile başlaması durumunda, çözümleyici noktalı virgül +**eklemez**. + + log('testing!') + (options.list || []).forEach(function(i) {}) + +Yukarıdaki program parçası aşağıdaki tek satıra dönüşür. + + log('testing!')(options.list || []).forEach(function(i) {}) + +**Büyük** ihtimalle yukarıdaki `log` bir fonksiyon **döndürmüyordur**; +bu nedenle, yukarıdaki satır `undefined is not a function` hata mesajı ile bir +`TypeError` oluştumasına neden olacaktır. + +### Sonuç + +Noktalı virgüllerin **hiç bir zaman** ihmal edilmemesi tavsiye edilir, ayrıca +ayraçların kendilerinden önceki ifade ile aynı satırda tutulması ve tek satırlık +`if` ve `else` ifadelerinde bile ayraçların ihmal edilmemesi önerilir. Her iki +önlem de hem programın tutarlılığını artıracak, hem de JavaScript +çözümleyicisinin programın davranışını değiştirmesini engelleyecektir. + diff --git a/external/JavaScript-Garden/doc/tr/core/undefined.md b/external/JavaScript-Garden/doc/tr/core/undefined.md new file mode 100644 index 0000000..6ad70c0 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/core/undefined.md @@ -0,0 +1,71 @@ +## `undefined` ve `null` + +JavaScript'te `tanımsız` anlamına gelen iki değer vardır, ve bunlardan +`undefined` daha kullanışlıdır. + +### `undefined` değeri + +`undefined` bir değişken türüdür ve tek bir değere sahip olabilir: `undefined`. + +JavaScript'te ayrıca değeri `undefined` olan bir de genel kapsam değişkeni +tanımlanmıştır ve bu değişkenin adı da `undefined`'dır. Fakat bu değişken +bir sabit yada dilin anahtar kelimelerinden biri **değildir**. Yani bu +değişkenin *değeri* kolayca değiştirilebilir. + +> **ES5 Notu:** ECMAScript 5'e göre mutlak modda `undefined`'ın değeri +> *değiştirilemez*, fakat mesela adı `undefined` olan bir fonksiyon ile +> `undefined` değişkeni gizlenebilir. + +`undefined` değerinin verildiği durumlara bazı örnekler: + + - Genel kapsamlı `undefined` değişkeninin (değiştirilmedi ise) değeri + - `return` ifadesi içermeyen fonksiyonların verdiği değer + - Bir değer döndürmeyen `return` ifadeleri + - Mevcut olmayan nesne özellikleri + - Değer atanmamış fonksiyon parametreleri + - Değeri `undefined` olarak atanmış değişkenler + +### `undefined` değerinin değiştirilmesi durumu + +Genel kapsamdaki `undefined` değişkeni asıl `undefined` *değerinin* kopyasını +tuttuğu için, bu değeri değiştirmek `undefined` *değişken türünün* değerini +**değiştirmez**. + +Fakat, bir şeyi `undefined` ile karşılaştırmak için önce `undefined`'ın değerini +geri almak gerekir. + +Programı `undefined` değişkeninin değiştirilmesi olasılığına karşı korumak için +uygulanan yaygın bir yöntem [isimsiz bir fonksiyona](#function.scopes) +kullanılmayan bir parametre eklemektir. + + var undefined = 123; + (function(something, foo, undefined) { + // lokal kapsamda undefined değişkeni + // yine undefined değerine sahip + + })('Hello World', 42); + +Benzer bir yöntem yine isimsiz fonksiyonun içinde değer atanmamış bir değişken +deklare etmektir. + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +Buradaki tek fark program sıkıştırılırsa ortaya çıkacaktır, eğer fonksiyonun +başka bir yerinde `var` ifadesi kullanılmıyorsa fazladan 4 bayt kullanılmış +olacaktır. + +### `null` kullanımı + +JavaScript dilinde `undefined` geleneksel *null* yerine kullanılmaktadır, asıl +`null` (hem `null` değişmezi hem de değişken türü) ise kabaca başka bir +veri türüdür. + +`null` JavaScript içinde kapalı olarak kullanılır (mesela prototip zincirinin +sonuna gelindiği `Foo.prototype = null` ile belirtilir), fakat hemen her durumda +bunun yerine `undefined` kullanılabilir. + diff --git a/external/JavaScript-Garden/doc/tr/function/arguments.md b/external/JavaScript-Garden/doc/tr/function/arguments.md new file mode 100644 index 0000000..dbd129e --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/arguments.md @@ -0,0 +1,124 @@ +## `arguments` Nesnesi + +JavaScript'te her fonksiyon kapsamında `arguments` adlı özel bir nesne +tanımlıdır. Bu nesne fonksiyon çağrılırken verilen argümanların listesini +içerir. + +> **Not:** Eğer `arguments` adında bir değişken fonksiyon kapsamında bir `var` +> ifadesi ile veya tanımlı parametre olarak zaten mevcutsa, `arguments` nesnesi +> oluşturulmaz. + +`arguments` nesnesi bir `Array` *değildir*. Bir dizinin özelliklerinin bir +kısmına sahip olsa da (`length` özelliği) `Array.prototype` sınıfından +türetilmemiştir, aslında bir `Object` bile değildir. + +Bu nedenle, `arguments` nesnesi üzerinde `push`, `pop` ve `slice` gibi standart +dizi metotlarını kullanmak mümkün **değildir**. Klasik `for` döngüsü `arguments` +nesnesi ile kullanılabilir, ancak standart dizi metotlarını kullanmak için +gerçek bir diziye dönüştürmek gerekir. + +### Diziye dönüştürmek + +Aşağıdaki program parçası `arguments` nesnesinin tüm elemanlarına sahip yeni bir +dizi verecektir. + + Array.prototype.slice.call(arguments); + +Bu dönüşüm **yavaştır**, ve performansın belirleyici olduğu durumlarda +kullanılması **tavsiye olunmaz**. + +### Argümanların geçirilmesi + +Aşağıdaki örnekte, argümanların bir fonksiyondan diğerine geçirilmesi +için önerilen yöntem gösterilmiştir. + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // do stuff here + } + +Bir başka püf noktası da `call` ve `apply` 'ı birlikte kullanarak hızlı, +ilişkisiz fonksiyonlar yaratmaktır. + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // "method" 'un ilişkisiz bir versiyonunu yarat + // Aldığı parametreler: this, arg1, arg2...argN + Foo.method = function() { + + // Sonuç: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### Tanımlı parametreler ve argüman indisleri + +`arguments` nesnesi her iki özelliği ve fonksiyonun tanımlı parametreleri için +*getter* ve *setter* fonksiyonlar oluşturur. + +Sonuç olarak, bir tanımlı parametrenin değerini değiştirmek `arguments` +nesnesindeki karşılık gelen özelliğin değerini de değiştirecektir. + + 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); + +### Performans mitleri ve gerçekler + +`arguments` nesnesi fonksiyon kapsamında bir değişken veya tanımlı parametre +olarak kullanılmış olması durumları dışında her zaman oluşturulur. Kullanılıp +kullanılmaması fark etmez. + +*getter* ve *setter* fonksiyonlar **her zaman** oluşturulur; dolayısıyla +`arguments` nesnesini kullanmanın performans üzerinde olumsuz bir etkisi yoktur, +özellikle de sadece `arguments` nesnesinin özelliklerine erişmekten ibaret +olmayan *gerçek* programlarda. + +> **ES5 Notu:** Söz konusu *getter* ve *setter* fonksiyonlar mutlak modda +> oluşturulmaz. + + +Fakat, modern JavaScript motorlarının performansını ciddi bir şekilde etkileyen +bir durum vardır. Bu durum `arguments.callee` nesnesinin kullanılmasıdır. + + function foo() { + arguments.callee; // içinde olduğumuz fonksiyon nesnesi + arguments.callee.caller; // ve çağıran fonksiyon nesnesi + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // Normalde inline edilirdi... + } + } + +Yukarıdaki program parçasında, `foo` fonksiyonuna [inlining][1] uygulanması +mümkün değildir çünkü fonksiyonun hem kendisini ve kendisini çağıran fonksiyonu +bilmesi gerekmektedir. Bu yüzden hem inlining yapılamadığı için bir performans +artışı sağlanamamış hem de kapsüllenme bozulmuş olmaktadır, çünkü fonksiyon +artık kendisini çağıran kapsama bağımlı hale gelmiş olabilir. + +`arguments.callee` ve özelliklerinin **asla** kullanılmaması +**şiddetle tavsiye olunur**. + +> **ES5 Notu:** Mutlak modda `arguments.callee` kullanımı kaldırılmıştır ve +> kullanılması durumunda bir `TypeError` hatası oluşacaktır. + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/external/JavaScript-Garden/doc/tr/function/closures.md b/external/JavaScript-Garden/doc/tr/function/closures.md new file mode 100644 index 0000000..efd1feb --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/closures.md @@ -0,0 +1,100 @@ +## Closure ve Referanslar + +JavaScript'in en güçlü özelliklerinden biri de `closure` 'lara sahip olmasıdır. +Bunun anlamı her hangi bir kapsamın **her zaman** kendisini içeren kapsama +erişebilmesidir. JavaScript'te tek kapsam [fonksiyon kapsamı](#function.scopes) +olduğu için temelde tüm fonksiyonlar `closure` 'durlar. + +### Private değişkenler + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +Burada, `Counter` **iki** `closure` verir: `increment` fonksiyonu ve `get` +fonksiyonu. Bu iki fonksiyon da `Counter` fonksiyonun kapsamına ait bir +**referans** 'a sahiptir, ve bu nedenle söz konusu kapsamda tanımlanmış olan +`count` değişkenine erişebilirler. + +### Private değişkenler nasıl işler + +JavaScript'te kapsam referanslarına erişmek yada atama yapmak mümkün olmadığı +için, dış kapsamdan `count` değişkenine ulaşmak **mümkün değildir**. Bu +değişkene ulaşmanın tek yolu yukarıdaki iki `closure` 'dur. + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +Bu program parçası `Counter` fonksiyonun kapsamındaki `count` değişkeninin +değerini **değiştirmez**, çünkü `foo.hack` **bu kapsamda** tanımlanmamıştır. +Bunun yerine *global* kapsamda yeni bir değişen oluşturur (yada mevcut bir +değişkeni değiştirir). + +### Döngü içinde closure + +Sık yapılan bir hata, döngü içinde closure kullanıp döngünün indeks değişkeninin +değerinin kopyalanacağını varsaymaktır. + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +Yukarıdaki örnek çıktı olarak `0` - `9` arası sayıları vermek yerine, `10` +sayısını on kez yazacaktır. + +İçteki *isimsiz* fonksiyon `i` değişkeninin değerine değil referansına sahiptir +ve `console.log` çağrıldığında, `for` döngüsü çoktan tamamlanmış ve `i` +değişkeninin değeri `10` olmuştur. + +İstenen davranışı elde etmek için `i` değişkeninin değerinin **kopyalanması** +gerekir. + +### Referans probleminin çözümü + +Döngünün indeks değişkeninin değerini kopyalamanın en iyi yolu bir +[isimsiz fonksiyon](#function.scopes) kullanmaktır. + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +Dıştaki isimsiz fonksiyon her adımda çağrılacak ve `e` parametresi olarak +`i` 'nin **değerinin** bir kopyası verilecektir. + +`setTimeOut` fonksiyonuna verilen isimsiz fonksiyon artık `e` 'ye ait bir +referansa sahip olacaktır, ve referansın değeri döngü tarafından +**değiştirilmeyecektir**. + +Bu davranışı başka bir yolla da elde etmek mümkündür; isimsiz fonksiyondan başka +bir fonksiyon döndürmek. Bu durumda yukarıdaki ile aynı davranış elde +edilecektir. + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + diff --git a/external/JavaScript-Garden/doc/tr/function/constructors.md b/external/JavaScript-Garden/doc/tr/function/constructors.md new file mode 100644 index 0000000..e4913ab --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/constructors.md @@ -0,0 +1,124 @@ +## Nesne Oluşturucular + +JavaScript'te oluşturucular diğer dillerden farklıdır. Başında `new` bulunan +her fonksiyon çağrısı bir oluşturucudur. + +Oluşturucunun (çağrılan fonksiyonun) içinde `this` 'in değeri yeni yaratılan +`Object` 'dir. Bu **yeni** nesnenin [`prototipi`](#object.prototype) oluşturucu +olarak çağrılan fonksiyon nesnesinin prototipidir. + +Çağrılan fonksiyonda bir `return` ifadesi yoksa, `this` (yani yeni nesneyi) +döndürür. + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +Yukarıdaki program `Foo` oluşturucusunu çağırır ve yeni yaratılan nesnenin +`prototipini` `Foo.prototype` olarak belirler. + +Oluşturucunun içinde bir `return` ifadesi bulunması durumunda, **ve sadece** +bu değer bir `Object` ise oluşturucu fonksiyon verilen değeri döndürür. + + function Bar() { + return 2; + } + new Bar(); // yeni bir Bar nesnesi + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // döndürülen nesne + +`new` anahtar kelimesi ihmal edilirse, fonksiyon yeni bir nesne **döndürmez**. + + function Foo() { + this.bla = 1; // global nesnenin özelliğini değiştirir + } + Foo(); // undefined + +Yukarıdaki örnek bazı durumlarda doğru çalışıyor gibi görünebilir, ama +JavaScript'te [`this`](#function.this) 'in çalışma şeklinden dolayı `this` +'in değeri *global nesne* olacaktır. + +### Nesne fabrikaları + +`new` anahtar kelimesini ihmal edebilmek için oluşturucu fonksiyonun bir değer +döndürmesi gerekir. + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +Yukarıda `Bar` fonksiyonunu çağıran her iki ifade de aynı şeyi döndürecektir: +`method` adında bir [`closure`](#function.closures) özelliği olan yeni yaratılmış +bir nesne. + +Başka bir nokta da `new Bar()` fonksiyonunun döndürülen nesnenin prototipini +**etkilememesidir**. Yeni nesnenin prototipi oluşturulacaktır ancak `Bar` bu +nesneyi döndürmez. + +Yukarıdaki örnekte `new` anahtar kelimesini kullanmakla kullanmamak arasında +hiçbir bir fark yoktur. + +### Fabrikalar ile yeni nesneler oluşturmak + +`new` anahtar kelimesinin **kullanılmaması** tavsiye edilir, çünkü unutulması +durumu hatalara sebep olabilir. + +Bunun yerine yeni bir nesne oluşturmak için bir fabrika kullanılmalıdır. + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +Yukarıdaki örnek hem `new` anahtar kelimesinin unutulmasından etkilenmez hem de +[private değikenlerin](#function.closures) kullanılmasını kolaylaştırır, ama +bazı dezavantajları da vardır. + + 1. Oluşturulan nesneler bir prototip üzerinde metotlarını **paylaşmadıkları** + için daha fazla hafıza kullanılır. + 2. Başka bir sınıf türetmek için fabrikanın tüm metotları başka bir nesneden + kopyalaması veya bu nesneyi yeni nesnenin prototipine yerleştirmesi gerekir. + 3. Sadece `new` anahtar kelimesinin ihmal edilmesinden kaynaklanacak sorunları + gidermek için prototip zincirinden vazgeçmek dilin ruhuna aykırıdır. + +### Sonuç + +`new` anahtar kelimesini ihmal etmek hatalara neden olabilir, fakat bu +kesinlikle prototip zincirinden vazgeçmek için bir neden **olamaz**. Hangi +çözümün belirli bir programa uygun olduğu kararını verirken, en önemli nokta +nesne oluşturmak için belirli bir yöntemi seçip bu çözüme **bağlı kalmaktır**. + diff --git a/external/JavaScript-Garden/doc/tr/function/general.md b/external/JavaScript-Garden/doc/tr/function/general.md new file mode 100644 index 0000000..84d5480 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/general.md @@ -0,0 +1,49 @@ +## Fonksiyon Tanımlaması ve Fonksiyon İfadesi + +Fonksiyonlar JavaScript'te birinci sınıf nesnelerdir, yani sıradan bir değer +gibi kullanılabilirler. Bu özellik sıklıkla bir *isimsiz fonksiyonu* başka bir +fonksiyona - ki bu muhtemelen asenkron bir fonksiyondur - `callback` olarak +geçirmekte kullanılır. + +### `function` tanımlaması + + function foo() {} + +Yukarıdaki fonksiyon tanımlaması program çalışmadan önce +[yukarı taşınır](#function.scopes) ve böylece *tanımlandığı* kapsam içinde +*her yerde* (hatta tanımlanmadan önce bile) kullanılabilir. + + foo(); // foo bu satır çalışmadan önce oluşturuldu + function foo() {} + +### `function` ifadesi + + var foo = function() {}; + +Bu örnekte *isimsiz fonksiyon* `foo` değişkenine atanır. + + foo; // 'undefined' + foo(); // Bu satır bir TypeError hatasına neden olur + var foo = function() {}; + +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 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 + +Bir başka özel durum isimli fonksiyon ifadesidir. + + var foo = function bar() { + bar(); // Çalışır + } + bar(); // ReferenceError hatası verir + +Burada `bar` fonksiyonuna dış kapsamdan ulaşılamaz, çünkü sadece `foo` +değişkenine atanmıştır; fakat iç kapsamda `bar` fonksiyonuna erişilebilir. +Bunun nedeni JavaScript'te [isim çözümlemenin](#function.scopes) çalışma +şeklidir, fonksiyonun adına fonksiyonun içinden *her zaman* erişilebilir. + diff --git a/external/JavaScript-Garden/doc/tr/function/scopes.md b/external/JavaScript-Garden/doc/tr/function/scopes.md new file mode 100644 index 0000000..211c45f --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/scopes.md @@ -0,0 +1,232 @@ +## Kapsamlar ve İsim Uzayları + +JavaScript'te birbiri ile eşleşen ayraçlar kullanılmasına karşın blok +kapsamı **bulunmaz**; bu nedenle, dilde sadece *fonksiyon kapsamı* mevcuttur. + + function test() { // fonksiyon kapsamı + for(var i = 0; i < 10; i++) { // kapsam değil + // sayaç + } + console.log(i); // 10 + } + +> **Not:** Bir değer atama, `return` ifadesi veya fonksiyon argümanı olarak +> kullanıldığında `{...}` notasyonu bir nesne değişmezi olarak **değil** +> blok ifade olarak değerlendirilir. Bu özellik +> [otomatik noktalı virgül ilavesi](#core.semicolon) ile birleştiğinde fark +> edilmesi zor hatalara neden olabilir. + +JavaScript'te isim uzayları kavramı da bulunmaz, tanımlanan herşey +*genel olarak paylaşılmış* tek bir isim uzayının içindedir. + +Bir değişkene erişildiğinde, JavaScript değişkenin tanımını bulana dek yukarıya +doğru tüm kapsamlara bakar. Genel kapsama ulaşıldığı halde hala değişkenin +tanımı bulanamamışsa bir `ReferenceError` hatası oluşur. + +### Genel değişkenler felaketi + + // A programı + foo = '42'; + + // B programı + var foo = '42' + +Yukarıdaki iki program birbirinden **farklıdır**. A programında *genel* kapsamda +bir `foo` değişkeni tanımlanmıştır, B programındaki `foo` değişkeni ise *mevcut* +kapsamda tanımlanmıştır. + +Bu iki tanımlamanın birbirinden **farklı** *etkileri* olacaktır, `var` anahtar +kelimesini kullanmamanın önemli sonuçları olabilir. + + // genel kapsam + var foo = 42; + function test() { + // lokal kapsam + foo = 21; + } + test(); + foo; // 21 + +`test` fonksiyonun içinde `var` anahtar kelimesinin atlanması genel kapsamdaki +`foo` değişkeninin değerini değiştirecektir. İlk bakışta bu önemsiz gibi görünse +de, binlerce satırlık bir programda `var` kullanılmaması korkunç ve takibi güç +hatalara neden olacaktır. + + // genel kapsam + var items = [/* bir dizi */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // subLoop fonksiyonun kapsamı + for(i = 0; i < 10; i++) { // var kullanılmamış + // do amazing stuff! + } + } + +Dışarıdaki döngüden `subLoop` fonksiyonu bir kez çağrıldıktan sonra çıkılacaktır, +çünkü `subLoop` `i` değişkeninin dış kapsamdaki değerini değiştirir. İkinci +`for` döngüsünde de `var` kullanılması bu hatayı kolayca engelleyecektir. +*Bilinçli olarak* dış kapsama erişilmek istenmiyorsa `var` ifadesi **asla** +atlanmamalıdır. + +### Lokal değişkenler + +JavaScript'te lokal değişkenler sadece [fonksiyon](#function.general) +parametreleri ve `var` ifadesi ile tanımlanan değişkenlerdir. + + // genel kapsam + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // test fonksiyonunun lokal kapsamı + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`test` fonksiyonun içinde `foo` ve `i` lokal değişkenlerdir, `bar` değişkenine +değer atanması ise genel kapsamdaki aynı isimdeki değişkenin değerini +değiştirecektir. + +### Yukarı taşıma + +JavaScript'te tanımlamalar **yukarı taşınır**. Yani hem `var` ifadesi hem de +`function` bildirimleri içindeki bulundukları kapsamın en üstüne taşınırlar. + + 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]; + } + } + +Program çalışmadan önce yukarıdaki kod dönüştürülür. JavaScript, `var` +ifadelerini ve `function` bildirimlerini içinde bulundukları kapsamın en üstüne +taşır. + + // var ifadeleri buraya taşınır + var bar, someValue; // varsayılan değerleri 'undefined' olur + + // function bildirimi de yukarı taşınır + function test(data) { + var goo, i, e; // blok kapsamı olmadığı için buraya taşınır + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // bir TypeError hatası oluşur çünkü bar hala 'undefined' + someValue = 42; // değer atamaları etkilenmez + bar = function() {}; + + test(); + +Blok kapsamının bulunmaması nedeniyle hem `var` ifadeleri döngülerin dışına +taşınır hem de bazı `if` ifadeleri anlaşılmaz sonuçlar verebilir. + +Orijinal programda `if` ifadesi `goo` isimli *genel değişkeni* değiştiriyor gibi +görünüyordu, fakat yukarı taşımadan sonra anlaşıldığı gini aslında +*lokal değişkeni* değiştiriyor. + +*Yukarı taşıma* dikkate alınmadığında aşağıdaki programın bir `ReferenceError` +oluşturacağı sanılabilir. + + // SomeImportantThing değişkenine değer atanmış mı, kontrol et + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +Fakat `var` değişkeni *genel kapsamın* en üstüne taşınacağı için bu program +çalışacaktır. + + var SomeImportantThing; + + // SomeImportantThing arada bir yerde atanmış olabilir + + // Değer atandığından emin ol + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### İsim çözümleme + +JavaScript'te *genel kapsam* da dahil tüm kapsamlarda [`this`](#function.this) +adında bir özel değişken tanımlanmıştır, bu değişken *geçerli nesneyi* gösterir. + +Fonksiyon kapsamlarında aynı zamanda [`arguments`](#function.arguments) adında +bir değişken tanımlanmıştır ve fonksiyonun argümanlarını içerir. + +Örnek olarak bir fonksiyon kapsamında `foo` değişkenine erişildiğinde JavaScript +isim çözümlemeyi aşağıdaki sıra ile yapacaktır: + + 1. Geçerli kapsamda bir `var foo` ifadesi mevcutsa bu kullanılır. + 2. Fonksiyonun parametrelerinden birinin adı `foo` ise bu kullanılır. + 3. Fonksiyonun kendisinin adı `foo` ise bu kullanılır. + 4. Bir dıştaki kapsama geçilir ve yeniden **1** adımına dönülür. + +> **Not:** `arguments` adında bir parametre bulunması durumunda varsayılan +> `arguments` nesnesi **oluşturulmayacaktır**. + +### İsim uzayları + +Tek bir genel isim uzayının bulunmasının yol açtığı yaygın sonuç isim +çakışmasıdır. JavaScript'te bu sorun *isimsiz fonksiyonlar* ile kolayca +önlenebilir. + + (function() { + // bir "isim uzayı" + + window.foo = function() { + // korunmasız bir closure + }; + + })(); // fonksiyonu hemen çalıştır + +İsimsiz fonksiyonlar [ifade](#function.general) olarak değerlendirilir; +bu nedenle çağrılabilmeleri için önce değerlendirilmeleri gerekir. + + ( // parantezin içindeki fonksiyonu değerlendir + function() {} + ) // ve fonksiyon nesnesini döndür + () // değerlendirmenin sonucu fonksiyon nesnesini çağır + +Bir fonksiyon ifadesini değerlendirip çağırmanın başka yolları da vardır ve +yukarıdaki ile aynı sonucu verirler. + + // İki farklı yöntem + +function(){}(); + (function(){}()); + +### Sonuç + +Programı kendi isim uzayı ile kapsamak için her zaman *isimsiz fonksiyonların* +kullanılması tavsiye edilir. Böylece hem isim çakışmalarından korunulmuş olunur, +hem de programlar daha modüler halde yazılmış olur. + +Ayrıca, genel değişkenlerin kullanılması **kötü bir uygulamadır**. Genel +değişkenlerin *herhangi bir şekilde* kullanılmış olması programın kötü yazılmış +olduğuna, hatalara eğilimli olduğuna ve sürdürülmesinin zor olacağına işaret +eder. + diff --git a/external/JavaScript-Garden/doc/tr/function/this.md b/external/JavaScript-Garden/doc/tr/function/this.md new file mode 100644 index 0000000..b589dc4 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/function/this.md @@ -0,0 +1,115 @@ +## `this` Nasıl Çalışır + +JavaScript'te `this` özel kelimesinin anlamı diğer programlama dillerinden +farklıdır. `this` kelimesinin birbirinden farklı anlamlar yüklendiği tam +**beş** durum vardır. + +### Genel kapsam + + this; + +`this` kelimesi genel kapsamda kullanıldığında *global* nesneye işaret eder. + + +### Bir fonksiyon çağırma + + foo(); + +Burada `this` yine *global* nesneye işaret eder. + +> **ES5 Notu:** Mutlak modda bu davranış **kaldırılmıştır**. Bu durumda `this` +> kelimesinin değeri `undefined` olacaktır. + +### Bir metod çağırma + + test.foo(); + +Bu örnekte `this` kelimesi `test` 'e işaret edecektir. + +### Bir nesne oluşturucu çağırma + + new foo(); + +Bir fonksiyon başında `new` anahtar kelimesi ile birlikte çağrılırsa bir +[nesne oluşturucu](#function.constructors) olarak davranır. Bu fonksiyonun +içinde `this` kelimesi *yeni oluşturulan* `Object` 'e işaret eder. + +### `this` kelimesinin atanması + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // dizi aşağıdaki gibi açılır + foo.call(bar, 1, 2, 3); // sonuç: a = 1, b = 2, c = 3 + +`Function.prototype` 'ın `call` veya `apply` metodları kullanıldığında, çağrılan +fonksiyonun içinde `this` 'in değeri ilk argümanın değeri olarak **atanır**. + +Sonuç olarak, yukarıdaki örnekte *metod çağırma* durumu geçerli **olmayacak**, +bunun yerine `foo` fonksiyonu içinde `this` 'in değeri `bar` olacaktır. + +> **Not:** `this` kelimesi bir `Object` sabiti içinde nesnenin kendisine +> işaret etmek için **kullanılamaz**. Yani `var obj = {me: this}` gibi bir +> ifadede `me`, `obj` nesnesine işaret **etmeyecektir**, `this` sadece yukarıda +> açıklanan beş durumdan biri ile kullanılabilir. + +### Sık düşülen yanılgılar + +Yukarıdaki durumların çoğu mantıklı görünse bile, ilk durum dilin tasarım +hatalarından biri olarak değerlendirilmelidir çünkü **hiçbir** pratik +kullanılımı yoktur. + + + Foo.method = function() { + function test() { + // this genel nesneye işaret eder + } + test(); + }; + +Bir başka yanılgı `test` fonksiyonunun içinde `this` 'in `Foo` 'ya işaret +edeceğinin sanılmasıdır, ama bu **doğru değildir**. + +`test` fonksiyonu içinden `Foo` 'ya erişmenin yolu `method` içinde bir lokal +değişken oluşturmaktır. + + Foo.method = function() { + var that = this; + function test() { + // Burada this yerine that kullanın + } + test(); + }; + +`that` kelimesinin dilde özel bir anlamı yoktur, ama sıklıkla dış kapsamdaki +`this` 'e işaret etmek için kullanılır. Bu yöntem [`closure`](#function.closures) +kavramı ile birlikte kullanıldığında `this` değerini program içinde taşımaya da +yarar. + +### Metodları değişkenlere atamak + +JavaScript'te mevcut **olmayan** bir başka özellik de fonksiyon isimlendirmedir, +başka bir deyişle bir metodu bir değişkene **atamak**. + + var test = someObject.methodTest; + test(); + +İlk durum nedeniyle `test` artık sıradan bir fonksiyon olarak davranacaktır; bu +nedenle `test` fonksiyonu içinde `this` artık `someObject` 'e işaret +etmeyecektir. + +`this` kelimesinin geç bağlanması ilk bakışta yanlış görünse de, aslında +[prototipsel kalıtımı](#object.prototype) mümkün kılan şey budur. + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +Yukarıda `Bar` sınıfına ait bir nesnenin `method` 'u çağrıldığında `this` bu +nesneye işaret edecektir. + + diff --git a/external/JavaScript-Garden/doc/tr/index.json b/external/JavaScript-Garden/doc/tr/index.json new file mode 100644 index 0000000..9d04c42 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden Türkçe", + "description": "JavaScript'in Acayiplikleri ve Kusurları için bir Rehber.", + "sections": [ + { + "title": "Giriş", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "Nesneler", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "forinloop" + ] + }, + { + "title": "Fonksiyonlar", + "dir": "function", + "articles": [ + "general", + "this", + "closures", + "arguments", + "constructors", + "scopes" + ] + }, + { + "title": "Diziler", + "dir": "array", + "articles": [ + "general", + "constructor" + ] + }, + { + "title": "Nesne Tipleri", + "dir": "types", + "articles": [ + "equality", + "typeof", + "instanceof", + "casting" + ] + }, + { + "title": "Temel", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "Diğer", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/tr/intro/index.md b/external/JavaScript-Garden/doc/tr/intro/index.md new file mode 100644 index 0000000..4bf6a85 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/intro/index.md @@ -0,0 +1,47 @@ +## Giriş + +**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 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 +gerekir. Eğer JavaScript dilinin temellerini öğrenmek 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 + +- [Katkıda Bulunanlar](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## Sunum + +JavaScript Garden GitHub üzerinden, ve ayrıca [Cramer Development][7] +tarafından desteklenen [JavaScriptGarden.info][8] adresinden sunulmaktadır. + +## 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 + diff --git a/external/JavaScript-Garden/doc/tr/object/forinloop.md b/external/JavaScript-Garden/doc/tr/object/forinloop.md new file mode 100644 index 0000000..ab82e62 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/object/forinloop.md @@ -0,0 +1,49 @@ +## `for in` Döngüsü + +Tıpkı `in` operatörü gibi `for in` döngüsü de bir nesnenin özellikleri üzerinde +iterasyon yaparken prototip zincirini dolaşır. + +> **Not:** `for in` döngüsü iterasyon yaparken `enumerable` niteliği `false` +> olan özelliklere uğramaz; mesela, bir dizinin `length` özelliğini atlar. + + // Object.prototype'a bar özelliğini ekle + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // hem bar hem de moo yazar + } + +`for in` döngüsünün davranışını değiştirmek mümkün olmadığı için, istenmeyen +özelliklerin döngünün içinde filtrelenmesi gerekir, bu da `Object.prototype` +nesnesinin [`hasOwnProperty`](#object.hasownproperty) metodu ile yapılır. + +> **Not:** `for in` döngüsü tüm prototip zincirini dolaştığı için bir nesneye +> eklenen her yeni kalıtım katmanı döngüyü biraz daha yavaşlatacaktır. + +### `hasOwnProperty` kullanarak filtrelemek + + // yukarıdaki örnekteki foo nesnesi + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +Doğru kullanım bu yeni versiyonda gösterildiği gibidir. `hasOwnProperty` kontrol +edildiği için **sadece** `moo` yazacaktır. `hasOwnProperty` kullanılmaz ise ve +`Object.prototype` 'ın baz özellikleri değiştirilmişse, program bazı hatalara +yatkın olabilir. + +Bunu yapan ve yaygın olarak kullanılan bir JavaScript sistemi [Prototype][1] +'dır. Bu sistemde `hasOwnProperty` kullanmayan `for in` döngüleri kesinlikle +hatalı sonuç verecektir. + +### Sonuç + +`hasOwnProperty` **her zaman** kontrol edilmelidir. Programın içinde çalıştığı +ortam için, nesnelerin baz özelliklerinin değiştirilip değiştirilmediğine dair +hiçbir kabul yapılmamalıdır. + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/tr/object/general.md b/external/JavaScript-Garden/doc/tr/object/general.md new file mode 100644 index 0000000..fd99eca --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/object/general.md @@ -0,0 +1,102 @@ +## Nesne Kullanımı ve Özellikleri + +JavaScript'te iki istisna dışında her şey bir nesne olarak davranır; +bu istisnalar da [`null`](#core.undefined) ve [`undefined`](#core.undefined) +'dır. + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +Sık düşülen bir yanılgı sayı sabitlerinin nesne olarak kullanılamayacağıdır. Bu +yanılgının sebebi de JavaScript çözümleyicisinin *nokta notasyonu* ile girilen +sayıları bir reel sayı olarak algılama hatasıdır. + + 2.toString(); // SyntaxError hatası verir + +Bu hatayı aşıp sayı sabitlerinin de nesne olarak davranmasını sağlamak için +uygulanabilecek bazı çözümler vardır. + + 2..toString(); // ikinci nokta doğru şekilde algılanır + 2 .toString(); // noktanın solundaki boşluğa dikkat edin + (2).toString(); // ilk önce 2 değerlendirilir + +### Bir veri türü olarak nesneler + +JavaScript nesneleri aynı zamanda bir [*Hashmap*][1] olarak da kullanılabilir, +nesneler temelde isimli özellikler ve bunlara karşılık gelen değerlerden +ibarettir. + +Nesne sabiti (`{}` notasyonu) ile düz bir nesne yaratmak mümkündür. Bu yeni +nesne [kalıtım](#object.prototype) ile `Object.prototype` 'dan türüyecektir ve +hiçbir [baz özelliğe](#object.hasownproperty) sahip olmayacaktır. + + var foo = {}; // yeni bir boş nesne + + // adı 'test' ve değeri 12 olan bir özelliği sahip yeni bir nesne + var bar = {test: 12}; + +### Özelliklere erişmek + +Bir nesnenin özelliklerine iki yolla erişilebilir, ya nokta notasyonu ile veya +köşeli parantez notasyonu ile. + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // çalışır + +Her iki notasyon da aynı şekilde çalışır, tek fark köşeli parantez notasyonunun +özelliklerin dinamik olarak oluşturulmasına ve normalde bir yazım hatasına yol +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. + + 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]); + } + } + +Yukarıdaki örnek sonuç olarak hem `bar undefined` hem de `foo null` yazacaktır. +Sadece `baz` özelliği kaldırılmış olacak ve çıktıda görünmeyecektir. + +### Anahtar notasyonu + + var test = { + 'case': 'anahtar kelime olduğu için katar olarak girildi', + delete: 'yine bir anahtar kelime' // SyntaxError hatası + }; + +Nesne özellikleri düz karakterler olarak da katar notasyonu ile de +tanımlanabilir. Fakat JavaScript çözümleyicisinin bir başka tasarım hatası +yüzünden, yukarıdaki örnek ECMAScript 5 öncesinde bir `SyntaxError` hatası +verecektir. + +Bu hata `delete` 'in bir *anahtar kelime* olmasından kaynaklanır, bu nedenle +eski JavaScript motorlarının bu örneği doğru algılaması için *karakter katarı* +notasyonu ile girilmelidir. + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/tr/object/hasownproperty.md b/external/JavaScript-Garden/doc/tr/object/hasownproperty.md new file mode 100644 index 0000000..634b369 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/object/hasownproperty.md @@ -0,0 +1,56 @@ +## `hasOwnProperty` + +Bir özelliğin nesnenin [prototip zinciri](#object.prototype) üzerinde bir yerde +**değil**, *kendisi* üzerinde tanımlandığını belirlemek için, `Object.prototype` +kalıtımı ile tüm nesnelerin sahip olduğu `hasOwnProperty` metodunun kullanılması +gerekir. + +> **Not:** Bir özelliğin `undefined` olduğunu kontrol etmek yeterli **değildir**. +> Bir özelliğin değeri `undefined` olarak atandığı halde özelliğin kendisi +> pekala mevcut olabilir. + +`hasOwnProperty` JavaScript'te nesne özellikleri üzerinde çalışıp prototip +zincirinin tümünü **dolaşmayan** tek şeydir. + + // Object.prototype'a bar özelliğini ekle + Object.prototype.bar = 1; + var foo = {goo: undefined}; + + foo.bar; // 1 + 'bar' in foo; // true + + foo.hasOwnProperty('bar'); // false + foo.hasOwnProperty('goo'); // true + +Sadece `hasOwnProperty` beklenen doğru sonucu verecektir, nesne özellikleri +üzerinde iterasyon yaparken bu çok önemlidir. Bir nesnenin *kendisi* üzerinde +değil de protip zinciri üzerinde bir yerde tanımlanmış olan özelliklerini +çıkarmanın başka hiçbir yolu **yoktur**. + +### `hasOwnProperty` özelliği + +JavaScript `hasOwnProperty` adının bir özellik olarak kullanılmasını engellemez; +bu nedenle bir nesnenin bu isimde bir özelliğe sahip olması ihtimali varsa, +doğru sonuç alabilmek için `hasOwnProperty `*haricen* kullanılmalıdır. + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // her zaman false verir + + // hasOwnProperty başka bir nesne üzerinde + // kullanıldığında 'this' foo olur + ({}).hasOwnProperty.call(foo, 'bar'); // true + +### Sonuç + +Bir nesnenin bir özelliği sahip olup olmadığını kontrol etmek için +kullanılabilecek **tek** yöntem `hasOwnProperty` 'dir. Aynı zamanda, nesne +[prototiplerinin](#object.prototype) genişletilmesinden kaynaklanabilecek +hataların önüne geçmek için, **tüm** [`for in` döngüleri](#object.forinloop) ile +`hasOwnProperty` kullanılması tavsiye olunur. + diff --git a/external/JavaScript-Garden/doc/tr/object/prototype.md b/external/JavaScript-Garden/doc/tr/object/prototype.md new file mode 100644 index 0000000..d8b8522 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/object/prototype.md @@ -0,0 +1,116 @@ +## Prototip + +JavaScript klasik bir kalıtım modeli değil *prototip* modeli kullanır. + +Çoğu zaman bu modelin JavaScript'in zayıf yönlerinden biri olduğu söylense de, +aslında prototip model klasik modelden daha güçlüdür. Mesela prototip model +temel alınarak klasik kalıtım modeli oluşturulabilir, fakat bunun tersini yapmak +çok daha zordur. + +Prototip kalıtım modeli kullanan tek popüler dil JavaScript olduğu için iki +model arasındaki farklılıklara alışmak biraz zaman alır. + +İlk büyük farklılık JavaScript'te kalıtımın *prototip zincirleri* ile +yapılmasıdır. + +> **Not:** `Bar.prototype = Foo.prototype` gibi basit bir atama yapmak her iki +> nesnenin de **aynı** prototipe sahip olmasına neden olacaktır. Bu yüzden bir +> nesnenin prototipinde yapılacak değişiklikler diğer nesnenin prototipini de +> etkileyecektir, ki çoğu zaman istenen etki bu değildir. + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // Bar nesnesinin prototipi olarak yeni bir Foo nesnesini ata + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // Nesne oluşturucusunun Bar olmasını sağla + Bar.prototype.constructor = Bar; + + var test = new Bar() // yeni bir Bar oluştur + + // Sonuçta ortaya çıkan prototip zinciri + test [bir Bar sınıfı nesnesi] + Bar.prototype [bir Foo sınıfı nesnesi] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* vs. */ } + +Yukarıda, `test` nesnesi hem `Bar.prototype` hem de `Foo.prototype` 'dan +türeyecektir; bu nedenle `Foo` 'da tanımlanmış olan `method` fonksiyonuna +da erişebilir. Ayrıca, prototipi olan **tek** `Foo` nesnesinin `value` +özelliğine de erişebilir. Dikkat edilmesi gereken bir nokta, `new Bar()` +ifadesinin yeni bir `Foo` nesnesi **yaratmayıp**, prototipine atanmış olan +nesneyi kullanmasıdır; bu nedenle, tüm `Bar` nesneleri **aynı** `value` +özelliğine sahip olacaktır. + +> **Not:** `Bar.prototype = Foo` gibi bir ifade **kullanmayın**, çünkü `Foo` +> 'nun prototipine değil fonksiyon nesnesine işaret edecektir. Yani +> prototip zinciri `Foo.prototype` değil `Function.prototype` üzerinden +> gidecektir; ve bu yüzden, `method` prototip zincirinde bulunmayacaktır. + +### Özelliklere bulmak + +Bir nesnenin özelliklerine erişildiğinde, JavaScript, istenen isimdeki özelliği +bulana kadar prototip zincirinde **yukarı** doğru dolaşır. + +Zincirin en üstüne ulaştığında (yani `Object.prototype`) ve hala istenen özelliği +bulamamışsa sonuç olarak [`undefined`](#core.undefined) verecektir. + +### prototype özelliği + +`prototype` özelliği dil tarafından prototip zincirleri oluşturmak için +kullanılsa da, bu özelliğe **herhangi** bir değer atamak mümkündür. Fakat +prototip olarak atanan ilkel nesne türleri göz ardı edilecektir. + + function Foo() {} + Foo.prototype = 1; // hiç bir etkisi olmaz + +Bir önceki örnekte gösterildiği gibi, prototip olarak nesneler atanabilir, bu da +prototip zincirlerinin dinamik olarak oluşturulabilmesini sağlar. + +### Performans + +Prototip zincirinin yukarısındaki özellikleri aramanın performansı kritik olan +programlarda olumsuz etkileri olabilir. Ek olarak, mevcut olmayan özelliklere +erişmeye çalışmak da tüm prototip zincirinin baştan sona taranmasına neden +olacaktır. + +Ayrıca, bir nesnenin özellikleri üzerinde [iterasyon](#object.forinloop) +yapıldığında da prototip zinciri üzerindeki **tüm** özelliklere bakılacaktır. + +### Temel prototiplerin genişletilmesi + +Sıklıkla yapılan bir hata `Object.prototype` 'ı veya diğer baz prototipleri +genişletmektir. + +Bu tekniğe [*monkey patching*][1] denir ve *kapsüllemeyi* bozar. Bu teknik +[Prototype][2] gibi bazı popüler sistemlerde kullanılsa bile, temel nesne +türlerine *standart olmayan* özellikler eklenmesinin geçerli iyi bir nedeni +yoktur. + +Temel prototipleri genişletmenin **tek bir** geçerli nedeni vardır, o da daha +yeni JavaScript motorlarında bulunan özelliklerin eski motorlara getirilmesidir; +mesela [`Array.forEach`][3]. + +### Sonuç + +Prototip kalıtım modeli kullanan karmaşık programlar yazmadan önce bu modelin +tamamen anlaşılması **şarttır**. Ayrıca, prototip zincirinin uzunluğuna dikkat +edilmeli ve çok uzaması durumunda performans sorunları yaşamamak için parçalara +bölünmelidir. Bundan başka, temel prototipler yeni JavaScript motorları ile +uyumluluk sağlamak dışında bir nedenle **asla** genişletilmemelidir. + +[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/external/JavaScript-Garden/doc/tr/other/timeouts.md b/external/JavaScript-Garden/doc/tr/other/timeouts.md new file mode 100644 index 0000000..ef5de1c --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/other/timeouts.md @@ -0,0 +1,160 @@ +### `setTimeout` ve `setInterval` + +JavaScript asenkron olduğu için `setTimeout` ve `setInterval` kullanarak bir +fonksiyonun ileri bir zamanda çalışmasını sağlamak mümkündür. + +> **Not:** Zamanlama fonksiyonları ECMAScript Standartına dahil **değildir**, +> [DOM][1] ile birlikte tanımlanırlar. + + function foo() {} + var id = setTimeout(foo, 1000); // 0'dan büyük bir sayı verir + +Yukarıdaki örnekte `setTimeout` fonksiyonu çağrıldığında, oluşturulan +zamanlayıcı tanımlayan bir ID sayısı verir ve `foo` fonksiyonu **yaklaşık** +bin milisaniye sonra çalıştırılmak üzere programlanır. `foo` fonksiyonu +tam olarak **bir** kez çağrılacaktır. + +Kullanılan JavaScript motorunun zamanlayıcı hassasiyetine bağlı olarak, ve +ayrıca JavaScript tek `thread` ile çalıştığı ve çalışan başka program +parçaları bu tek `thread` 'i bloke edeceği için, `setTimeout` ile belirlenen +erteleme süresinin tam olarak gerçekleşeceği **hiçbir şekilde** garanti +edilemez. + +İlk argüman olarak verilen fonksiyon *global nesne* tarafından çağrılacaktır, +yani çağrılan fonksiyonun içinde [`this`](#function.this) bu nesneye işaret +edecektir. + + function Foo() { + this.value = 42; + this.method = function() { + // this global nesneye işaret eder + console.log(this.value); // undefined yazar + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **Not:** `setTimeout` fonksiyonunun ilk parametresi bir **fonksiyon nesnesi** +> olduğu için, sık yapılan bir hata `setTimeout(foo(), 1000)` şeklindeki +> kullanımdır, fakat bu şekilde, `foo` fonksiyonu **değil** `foo` fonksiyonunun +> **sonuç değeri** parametre olarak kullanacaktır. Bu kullanım genellikle bir +> hata mesajı üretmez, çünkü fonksiyon `undefined` değerini verdiğinde +> `setTimeout` bir hata **oluşturmayacaktır**. + +### `setInterval` ile fonksiyon çağrılarının yığılması + +`setTimeout` verilen fonksiyonu bir kez çağırırken, `setInterval` (adından da +anlaşılacağı gibi) verilen fonksiyonu **her** `X` milisaniyede bir çağırır. +Fakat kullanılması önerilmez. + +Mevcut program parçası çalışırken zamanlama bloke olduğu halde, `setInterval` +verilen fonksiyonu çağırmaya devam edecektir. Bu da, özellikle küçük aralıklarla +kullanıldığında, fonksiyon çağrılarının istiflenmesine neden olur. + + function foo(){ + // 1 saniye süren bir işlem + } + setInterval(foo, 100); + +Yukarıdaki örnekte `foo` fonksiyonu bir kez çağrılıp bir saniye boyunca bloke +edecektir. + +`foo` programı bloke etmişken, `setInterval` fonksiyon çağrılarını zamanlamaya +devam edecektir. `foo` tamamlandığında, çalıştırılmayı bekleyen **on** çağrı +daha olacaktır. + +### Bloke eden programlarla başa çıkmak + +En kolay ve kontrol edilebilir çözüm, `setTimeout` 'u fonksiyonun içinde +kullanmaktır. + + function foo(){ + // 1 saniye süren bir işlem + setTimeout(foo, 100); + } + foo(); + +Bu örnekte hem `setTimeout` çağrısı fonksiyonun kendisi içinde kapsanmış olmakta, +hem de fonksiyon çağrılarının istiflenmesinin önüne geçilerek daha fazla kontrol +sağlanmaktadır. Artık `foo` fonksiyonunun kendisi tekrar çalışmak isteyip +istemediğine karar verebilir. + +### Zamanlayıcıları iptal etmek + +Zamanlayıcıları iptal etmek için ilgili ID sayıları ile kullanılan zamanlayıcı +fonksiyonuna karşılık gelen `clearTimeout` ve `clearInterval` fonksiyonlarından +biri kullanılır. + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### Tüm zamanlayıcıları iptal etmek + +Tüm zamanlayıcıları iptal etmenin dahili bir yolu olmadığı için, bu amaca +ancak kaba kuvvetle ulaşılabilir. + + // "tüm" zamanlayıcıları iptal et + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +Bu rastgele seçilmiş sayıdan etkilenmeyen zamanlayıcılar kalabilir; bu yüzden +tüm zamanlayıcı ID'lerinin saklanarak, teker teker iptal edilmeleri tavsiye +edilir. + +### `eval` fonksiyonun gizli kullanımı + +`setTimeout` ve `setInterval` fonksiyonları ilk parametreleri olarak bir katar +da kabul eder. Bu özellik **asla** kullanılmamalıdır, çünkü bu durumda dahili +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. Mesela, Microsoft'un JScript motoru `eval` +> yerine `Function` oluşturucusunu kullanır. + + function foo() { + // setTimeOut ile bu fonksiyon çağrılacaktır + } + + function bar() { + function foo() { + // bu fonksiyon çağrılmayacaktır + } + setTimeout('foo()', 1000); + } + bar(); + +Bu durumda `eval` [direkt olarak](#core.eval) çağrılmadığı için, `setTimeout` +fonksiyonuna verilen katar *genel kapsamda* çalıştırılacaktır; bu nedenle, +`bar` fonksiyonu kapsamındaki lokal `foo` değişkenini kullanmayacaktır. + +Zamanlama fonksiyonlarına verilen fonksiyona argüman sağlamak için de bir katar +kullanılması tavsiye **edilmez**. + + function foo(a, b, c) {} + + // ASLA bu şekilde kullanılmamalı + setTimeout('foo(1, 2, 3)', 1000) + + // Bunu yerine isimsiz bir fonksiyon kullanın + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **Not:** `setTimeout(foo, 1000, 1, 2, 3)` sentaksının kullanılması da mümkün +> olmasına karşın tavsiye edilmez, çünkü bu kullanım [metodlarla](#function.this) +> birlikte fark edilmesi zor hatalara neden olabilir. + +### Sonuç + +`setTimeout` veya `setInterval` fonksiyonlarına **asla** bir katar parametre +verilmemelidir. Bu kullanım **çok** kötü bir programa işaret eder. Çağrılan +fonksiyona argümanlar verilmesinin gerektiği durumlarda gerçek çağrıyı içinde +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 "Document Object Model" diff --git a/external/JavaScript-Garden/doc/tr/types/casting.md b/external/JavaScript-Garden/doc/tr/types/casting.md new file mode 100644 index 0000000..b994c95 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/types/casting.md @@ -0,0 +1,72 @@ +## Tip Dönüşümleri + +JavaScript *weakly typed* bir dildir, bu yüzden **mümkün olan yerlerde** +*tip dönüşümü* uygular. + + // Bunlar true verir + new Number(10) == 10; // Number.toString() tekrar sayıya + // dönüştürülür + + 10 == '10'; // Katarlar sayıya dönüştürülür + 10 == '+10 '; // Bir başka katar çılgınlığı + 10 == '010'; // Ve bir tane daha + isNaN(null) == false; // null 0'a dönüştürülür + // tabii 0 NaN değildir + + // Bunlar false verir + 10 == 010; + 10 == '-10'; + +> **ES5 Notu:** `0` ile başlayan sayı sabitleri oktal (sekizlik) sayı sisteminde +> değerlendirilir. Oktal sayı desteği ECMAScript 5 mutlak modda +> **kaldırılmıştır**. + +Yukarıdakilerden kaçınmak için, [kesin eşitlik operatörünün](#types.equality) +kullanılması **şiddetle** tavsiye edilir. Böylece yaygın hataların çoğundan +kaçınılabilir, yine de JavaScript'in *weak typing* sisteminden kaynaklanan başka +sorunlar da vadır. + +### Temel tiplerin nesne oluşturucuları + +`Number` ve `String` gibi temel tiplerin nesne oluşturucuları `new` anahtar +kelimesi ile kullanılıp kullanılmamalarına göre farklı davranış gösterir. + + new Number(10) === 10; // False, Object ve Number + Number(10) === 10; // True, Number ve Number + new Number(10) + 0 === 10; // True, tip dönüşümü nedeniyle + +`Number` gibi bir temel tipin nesne oluşturucusunu kullanmak yeni bir `Number` +nesnesi yaratacaktır, fakat `new` kelimesi kullanılmazsa `Number` fonksiyonu +bir dönüştürücü olarak davranacaktır. + +Ayrıca, sabitler ve nesne olmayan değerler kullanılması durumunda başka tür +dönüşümler de söz konusu olacaktır. + +En iyi seçenek üç olası tipten birine **açıkça** dönüşüm yapılmasıdır. + +### Karakter katarına dönüştürmek + + '' + 10 === '10'; // true + +Bir değerin başına boş bir katar eklenerek kolayca katara dönüştürülebilir. + +### Sayıya dönüştürmek + + +'10' === 10; // true + +**Tek terimli** toplama operatörü kullanılarak bir değer sayıya dönüştürülebilir. + +### Mantıksal değişken tipine dönüştürmek + +**Değil** operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken +tipine dönüştürülebilir. + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/external/JavaScript-Garden/doc/tr/types/equality.md b/external/JavaScript-Garden/doc/tr/types/equality.md new file mode 100644 index 0000000..3240a54 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/types/equality.md @@ -0,0 +1,75 @@ +## Eşitlik ve Karşılaştırmalar + +JavaScript'de nesnelerin değerlerinin eşitliğini kontrol etmenin iki farklı yolu +vardır. + +### Eşitlik operatörü + +Eşitlik operatörü iki adet eşittir işaretinden oluşur: `==` + +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 + 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 + +Yukarıdaki tablo tip dönüşümünün sonuçlarını verir, ve `==` kullanımının kötü +bir uygulama olarak değerlendirilmesinin başlıca sebebidir. Bu karmaşık dönüşüm +kuralları tespit edilmesi zor hatalara neden olur. + +Ayrıca tip dönüşümü işin içine girdiğinde performans üzerinde de olumsuz etkisi +olur; mesela, bir katarın bir sayı ile karşılaştırılabilmesi için önce bir +sayıya dönüştürülmesi gerekir. + +### Kesin eşitlik operatörü + +Kesin eşitlik operatörü **üç adet** eşittir işaretinden oluşur: `===` + +Eşitlik operatörünün aksine, keşin eşitlik operatörü karşılaştırdığı değerler +arasında tip dönüşümü **yapmaz**. + + "" === "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 + +Yukarıdaki sonuçlar hem daha anlaşılırdır, hem de progamdaki hataların erkenden +ortaya çıkmasını sağlar. Bu programı bir miktar sağlamlaştırır ve ayrıca +karşılaştırılan değerlerin farklı tiplerden olması durumunda performansı da +artırır. + +### Nesneleri karşılaştırmak + +Hem `==` hem de `===` operatörlerinin **eşitlik** operatörü olarak +adlandırılmasına rağmen, değerlerden en azından birinin bir `Object` olması +durumunda farklı davranış gösterirler. + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +Bu durumda her iki operatör de eşitlik **değil** **aynılık** karşılaştırması +yapar; yani, terimlerin aynı nesnenin **örnekleri** olup olmadığını kontrol +ederler, tıpkı Python dilindeki `is` ve C dilindeki gösterici karşılaştırması +gibi. + +### Sonuç + +Sadece **kesin eşitlik** operatörünün kullanılması şiddetle tavsiye edilir. +Tip dönüşümü yapılmasının gerekli olduğu durumlarda, bu [açıkça](#types.casting) +yapılmalıdır ve dilin karmaşık dönüşüm kurallarına bırakılmamalıdır. + diff --git a/external/JavaScript-Garden/doc/tr/types/instanceof.md b/external/JavaScript-Garden/doc/tr/types/instanceof.md new file mode 100644 index 0000000..09f1923 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/types/instanceof.md @@ -0,0 +1,40 @@ +## `instanceof` Operatörü + +`instanceof` operatörü verilen iki terimin nesne oluşturucularını karşılaştırır. +Kullanışlı olduğu tek durum özel nesnelerin karşılaştırılmasıdır. Temel nesneler +üzerinde kullanıldığında neredeyse [typeof operatörü](#types.typeof) kadar +yararsızdır. + +### Özel nesneleri karşılaştırmak + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // Bu satır sadece Bar.prototype'a Foo fonksiyon nesnesinin atar + // Bir Foo sınıfı nesnesine değil + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +### Temel nesnelerle `instanceof` kullanımı + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +Dikkat edilmesi gereken ilginç bir nokta, `instanceof` operatörünün farklı +JavaScript kaynaklarından gelen nesneler üzerinde çalışmamasıdır (mesela bir +internet tarayıcısının farklı dökümanları), çünkü bu durumda nesne +oluşturucuları aynı olmayacaktır. + +### Sonuç + +`instanceof` operatörü **sadece** aynı JavaScript kaynağından gelen özel +nesneler ile kullanılmalıdır. Tıpkı [`typeof`](#types.typeof) operatöründe +olduğu gibi, bunun dışındaki tüm kullanımlarından **kaçınılmalıdır**. + diff --git a/external/JavaScript-Garden/doc/tr/types/typeof.md b/external/JavaScript-Garden/doc/tr/types/typeof.md new file mode 100644 index 0000000..62a9a72 --- /dev/null +++ b/external/JavaScript-Garden/doc/tr/types/typeof.md @@ -0,0 +1,87 @@ +## `typeof` Operatörü + +`typeof` operatörü ([`instanceof`](#types.instanceof) ile birlikte) +herhalde JavaScript'in en büyük tasarım hatalarından biridir, çünkü neredeyse +**tamamen arızalıdır**. + +`instanceof` operatörünün sınırlı kullanımı olsa da, `typeof` operatörünün +gerçekte tek bir pratik kullanımı vardır, ve bunun da bir nesnenin tipini +kontrol etmekle ilgili **yoktur**. + +> **Not:** `typeof` fonksiyon sentaksı (mesela ``typeof(obj)` gibi), ile de +> çağrılabilse de bu gerçek bir fonksiyon çağrısı değildir. İki parantezin +> içindeki ifadenin döndürdüğü değer typeof operatörüne verilir. `typeof` diye +> bir fonksiyon yoktur. + +### JavaScript tip tablosu + + Değer Sınıf Tip + ------------------------------------- + "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 + +Yukarıdaki tabloda *Tip* sütunu `typeof` operatörünün verdiği sonucu gösterir. +Açıkça görülebileceği gibi, bu sonuç tutarlı olmaktan çok uzaktır. + +*Sınıf* sütunu bir nesnenin dahili `[[Class]]` özelliğini gösterir. + +> **Spesifikasyondan:** `[[Class]]` özelliğinin değeri şu katarlardan biri +> olabilir: `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +`[[Class]]` özelliğinin değerini almak için `Object.prototype` 'ın `toString` +metodu kullanılmalıdır. + +### Bir nesnenin sınıfı + +Spesifikasyona göre `[[Class]]` değerine erişmenin tek yolu +`Object.prototype.toString` kullanmaktır. + + 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 + +Yukarıdaki örnekte, `Object.prototype.toString` çağrıldığında +[this](#function.this) 'in değeri `[[Class]]` değeri aranan nesne olarak +atanmış olmaktadır. + +> **ES5 Notu:** Kolaylık sağlamak için `Object.prototype.toString` 'in `null` +> ve `undefined` için verdiği değerler `Object` 'ten `Null` ve `Undefined` 'a +> **değiştirildi**. + +### Bir değişkenin tanımlandığını kontrol etmek + + typeof foo !== 'undefined' + +Yukarıdaki satır `foo` değişkeninin tanımlanıp tanımlanmadığını belirler; +tanımlanmamış bir değişkene erişmek bir `ReferenceError` hatası oluştur. +`typeof` operatörünün tek kullanışlı olduğu şey işte budur. + +### Sonuç + +Bir nesnenin tipini kontrol etmek için `Object.prototype.toString` 'in +kullanılması şiddetle tavsiye edilir; çünkü bunu yapmanın tek güvenilir yoludur. +Yukarıdaki tip tablosunda gösterildiği gibi, `typeof` operatörünün bazı +sonuçları spesifikasyonda tanımlanmamıştır; bu nedenle, çeşitli platformlarda +farklılık gösterebilirler. + +Bir değişkenin tanımlandığını kontrol etmek dışında, `typeof` operatörün +kullanımından **her ne pahasına olursa olsun** kaçınılmalıdır. + diff --git a/external/JavaScript-Garden/doc/zh/array/constructor.md b/external/JavaScript-Garden/doc/zh/array/constructor.md new file mode 100755 index 0000000..225c9f8 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/array/constructor.md @@ -0,0 +1,38 @@ +##`Array` 构造函数 + +由于 `Array` 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - `[]` - 来创建数组。 + + [1, 2, 3]; // 结果: [1, 2, 3] + new Array(1, 2, 3); // 结果: [1, 2, 3] + + [3]; // 结果: [3] + new Array(3); // 结果: [] + new Array('3') // 结果: ['3'] + + // 译者注:因此下面的代码将会使人很迷惑 + new Array(3, 4, 5); // 结果: [3, 4, 5] + new Array(3) // 结果: [],此数组长度为 3 + +> **译者注:**这里的模棱两可指的是数组的[两种构造函数语法][1] + +由于只有一个参数传递到构造函数中(译者注:指的是 `new Array(3);` 这种调用方式),并且这个参数是数字,构造函数会返回一个 `length` 属性被设置为此参数的空数组。 +需要特别注意的是,此时只有 `length` 属性被设置,真正的数组并没有生成。 + +> **译者注:**在 Firebug 中,你会看到 `[undefined, undefined, undefined]`,这其实是不对的。在上一节有详细的分析。 + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, 数组还没有生成 + +这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 `for` 循环的麻烦。 + + new Array(count + 1).join(stringToRepeat); + +> **译者注:** `new Array(3).join('#')` 将会返回 `##` + +###结论 + +应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。 + +[1]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array + diff --git a/external/JavaScript-Garden/doc/zh/array/general.md b/external/JavaScript-Garden/doc/zh/array/general.md new file mode 100755 index 0000000..5e222e8 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/array/general.md @@ -0,0 +1,55 @@ +##数组遍历与属性 + +虽然在 JavaScript 中数组是对象,但是没有好的理由去使用 [`for in` 循环](#object.forinloop) 遍历数组。 +相反,有一些好的理由**不去**使用 `for in` 遍历数组。 + +> **注意:** JavaScript 中数组**不是** *关联数组*。 +> JavaScript 中只有[对象](#object.general) 来管理键值的对应关系。但是关联数组是**保持**顺序的,而对象**不是**。 + +由于 `for in` 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 [`hasOwnProperty`](#object.hasownproperty) 函数, +因此会比普通的 `for` 循环慢上好多倍。 + +###遍历 + +为了达到遍历数组的最佳性能,推荐使用经典的 `for` 循环。 + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +上面代码有一个处理,就是通过 `l = list.length` 来缓存数组的长度。 + +虽然 `length` 是数组的一个属性,但是在每次循环中访问它还是有性能开销。 +**可能**最新的 JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。 + +实际上,不使用缓存数组长度的方式比缓存版本要慢很多。 + +###`length` 属性 + +`length` 属性的 *getter* 方式会简单的返回数组的长度,而 *setter* 方式会**截断**数组。 + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo; // [1, 2, 3] + +**译者注:** +在 Firebug 中查看此时 `foo` 的值是: `[1, 2, 3, undefined, undefined, undefined]` +但是这个结果并不准确,如果你在 Chrome 的控制台查看 `foo` 的结果,你会发现是这样的: `[1, 2, 3]` +因为在 JavaScript 中 `undefined` 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。 + + // 译者注:为了验证,我们来执行下面代码,看序号 5 是否存在于 foo 中。 + 5 in foo; // 不管在 Firebug 或者 Chrome 都返回 false + foo[5] = undefined; + 5 in foo; // 不管在 Firebug 或者 Chrome 都返回 true + +为 `length` 设置一个更小的值会截断数组,但是增大 `length` 属性值不会对数组产生影响。 + +###结论 + +为了更好的性能,推荐使用普通的 `for` 循环并缓存数组的 `length` 属性。 +使用 `for in` 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。 + diff --git a/external/JavaScript-Garden/doc/zh/core/eval.md b/external/JavaScript-Garden/doc/zh/core/eval.md new file mode 100755 index 0000000..577ba04 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/core/eval.md @@ -0,0 +1,67 @@ +##为什么不要使用 `eval` + +`eval` 函数会在当前作用域中执行一段 JavaScript 代码字符串。 + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +但是 `eval` 只在被**直接**调用并且调用函数就是 `eval` 本身时,才在当前作用域中执行。 + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +**[译者注][30]:**上面的代码等价于在全局作用域中调用 `eval`,和下面两种写法效果一样: + + // 写法一:直接调用全局作用域下的 foo 变量 + var foo = 1; + function test() { + var foo = 2; + window.foo = 3; + return foo; + } + test(); // 2 + foo; // 3 + + // 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域 + var foo = 1; + function test() { + var foo = 2; + eval.call(window, 'foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +在**任何情况下**我们都应该避免使用 `eval` 函数。99.9% 使用 `eval` 的场景都有**不使用** `eval` 的解决方案。 + +###伪装的 `eval` + +[定时函数](#other.timeouts) `setTimeout` 和 `setInterval` 都可以接受字符串作为它们的第一个参数。 +这个字符串**总是**在全局作用域中执行,因此 `eval` 在这种情况下没有被直接调用。 + + +###安全问题 + +`eval` 也存在安全问题,因为它会执行**任意**传给它的代码, +在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 `eval` 函数。 + +###结论 + +绝对不要使用 `eval`,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。 +如果一些情况必须使用到 `eval` 才能正常工作,首先它的设计会受到质疑,这**不应该**是首选的解决方案, +一个更好的不使用 `eval` 的解决方案应该得到充分考虑并优先采用。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/core/semicolon.md b/external/JavaScript-Garden/doc/zh/core/semicolon.md new file mode 100755 index 0000000..f4e5204 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/core/semicolon.md @@ -0,0 +1,100 @@ +##自动分号插入 + +尽管 JavaScript 有 C 的代码风格,但是它**不**强制要求在代码中使用分号,实际上可以省略它们。 + +JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来就解析源代码。 +因此 JavaScript 解析器在遇到由于缺少分号导致的解析错误时,会**自动**在源代码中插入分号。 + + var foo = function() { + } // 解析错误,分号丢失 + test() + +自动插入分号,解析器重新解析。 + + var foo = function() { + }; // 没有错误,解析继续 + test() + +自动的分号插入被认为是 JavaScript 语言**最大**的设计缺陷之一,因为它*能*改变代码的行为。 + +### 工作原理 + +下面的代码没有分号,因此解析器需要自己判断需要在哪些地方插入分号。 + + (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) + +下面是解析器"猜测"的结果。 + + (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; // <- 插入分号, 改变了 return 表达式的行为 + { // 作为一个代码段处理 + foo: function() {} + }; // <- 插入分号 + } + window.test = test; // <- 插入分号 + + // 两行又被合并了 + })(window)(function(window) { + window.someLibrary = {}; // <- 插入分号 + })(window); //<- 插入分号 + +> **注意:** JavaScript 不能正确的处理 `return` 表达式紧跟换行符的情况, +> 虽然这不能算是自动分号插入的错误,但这确实是一种不希望的副作用。 + +解析器显著改变了上面代码的行为,在另外一些情况下也会做出**错误的处理**。 + +###前置括号 + +在前置括号的情况下,解析器**不会**自动插入分号。 + + log('testing!') + (options.list || []).forEach(function(i) {}) + +上面代码被解析器转换为一行。 + + log('testing!')(options.list || []).forEach(function(i) {}) + +`log` 函数的执行结果**极大**可能**不是**函数;这种情况下就会出现 `TypeError` 的错误,详细错误信息可能是 `undefined is not a function`。 + +###结论 + +建议**绝对**不要省略分号,同时也提倡将花括号和相应的表达式放在一行, +对于只有一行代码的 `if` 或者 `else` 表达式,也不应该省略花括号。 +这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。 + diff --git a/external/JavaScript-Garden/doc/zh/core/undefined.md b/external/JavaScript-Garden/doc/zh/core/undefined.md new file mode 100755 index 0000000..9b779b6 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/core/undefined.md @@ -0,0 +1,60 @@ +##`undefined` 和 `null` + +JavaScript 有两个表示‘空’的值,其中比较有用的是 `undefined`。 + +###`undefined` 的值 + +`undefined` 是一个值为 `undefined` 的类型。 + +这个语言也定义了一个全局变量,它的值是 `undefined`,这个变量也被称为 `undefined`。 +但是这个变量**不是**一个常量,也不是一个关键字。这意味着它的*值*可以轻易被覆盖。 + +> **ES5 提示:** 在 ECMAScript 5 的严格模式下,`undefined` **不再是** *可写*的了。 +> 但是它的名称仍然可以被隐藏,比如定义一个函数名为 `undefined`。 + +下面的情况会返回 `undefined` 值: + + - 访问未修改的全局变量 `undefined`。 + - 由于没有定义 `return` 表达式的函数隐式返回。 + - `return` 表达式没有显式的返回任何内容。 + - 访问不存在的属性。 + - 函数参数没有被显式的传递值。 + - 任何被设置为 `undefined` 值的变量。 + +###处理 `undefined` 值的改变 + +由于全局变量 `undefined` 只是保存了 `undefined` 类型实际*值*的副本, +因此对它赋新值**不会**改变类型 `undefined` 的值。 + +然而,为了方便其它变量和 `undefined` 做比较,我们需要事先获取类型 `undefined` 的值。 + +为了避免可能对 `undefined` 值的改变,一个常用的技巧是使用一个传递到[匿名包装器](#function.scopes)的额外参数。 +在调用时,这个参数不会获取任何值。 + + var undefined = 123; + (function(something, foo, undefined) { + // 局部作用域里的 undefined 变量重新获得了 `undefined` 值 + + })('Hello World', 42); + +另外一种达到相同目的方法是在函数内使用变量声明。 + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +这里唯一的区别是,在压缩后并且函数内没有其它需要使用 `var` 声明变量的情况下,这个版本的代码会多出 4 个字节的代码。 + +> **[译者注][30]:**这里有点绕口,其实很简单。如果此函数内没有其它需要声明的变量,那么 `var` 总共 4 个字符(包含一个空白字符) +就是专门为 `undefined` 变量准备的,相比上个例子多出了 4 个字节。 + +###`null` 的用处 + +JavaScript 中的 `undefined` 的使用场景类似于其它语言中的 *null*,实际上 JavaScript 中的 `null` 是另外一种数据类型。 + +它在 JavaScript 内部有一些使用场景(比如声明原型链的终结 `Foo.prototype = null`),但是大多数情况下都可以使用 `undefined` 来代替。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/function/arguments.md b/external/JavaScript-Garden/doc/zh/function/arguments.md new file mode 100755 index 0000000..ac6967e --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/arguments.md @@ -0,0 +1,122 @@ +##`arguments` 对象 + +JavaScript 中每个函数内都能访问一个特别变量 `arguments`。这个变量维护着所有传递到这个函数中的参数列表。 + +> **注意:** 由于 `arguments` 已经被定义为函数内的一个变量。 +> 因此通过 `var` 关键字定义 `arguments` 或者将 `arguments` 声明为一个形式参数, +> 都将导致原生的 `arguments` 不会被创建。 + +`arguments` 变量**不是**一个数组(`Array`)。 +尽管在语法上它有数组相关的属性 `length`,但它不从 `Array.prototype` 继承,实际上它是一个对象(`Object`)。 + +因此,无法对 `arguments` 变量使用标准的数组方法,比如 `push`、`pop` 或者 `slice`。 +虽然使用 `for` 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。 + +###转化为数组 + +下面的代码将会创建一个新的数组,包含所有 `arguments` 对象中的元素。 + + Array.prototype.slice.call(arguments); + +这个转化比较**慢**,在性能不好的代码中**不推荐**这种做法。 + +###传递参数 + +下面是将参数从一个函数传递到另一个函数的推荐做法。 + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // 干活 + } + +另一个技巧是同时使用 `call` 和 `apply`,创建一个快速的解绑定包装器。 + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // 创建一个解绑定的 "method" + // 输入参数为: this, arg1, arg2...argN + Foo.method = function() { + + // 结果: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +**[译者注][30]**:上面的 `Foo.method` 函数和下面代码的效果是一样的: + + Foo.method = function() { + var args = Array.prototype.slice.call(arguments); + Foo.prototype.method.apply(args[0], args.slice(1)); + }; + + +###自动更新 + +`arguments` 对象为其内部属性以及函数形式参数创建 *getter* 和 *setter* 方法。 + +因此,改变形参的值会影响到 `arguments` 对象的值,反之亦然。 + + 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); + +### 性能真相 + +不管它是否有被使用,`arguments` 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。 + +`arguments` 的 *getters* 和 *setters* 方法总会被创建;因此使用 `arguments` 对性能不会有什么影响。 +除非是需要对 `arguments` 对象的属性进行多次访问。 + +> **ES5 提示:** 这些 *getters* 和 *setters* 在严格模式下(strict mode)不会被创建。 + +**[译者注][30]:**在 [MDC][2] 中对 `strict mode` 模式下 `arguments` 的描述有助于我们的理解,请看下面代码: + + // 阐述在 ES5 的严格模式下 `arguments` 的特性 + function f(a) { + "use strict"; + a = 42; + return [a, arguments[0]]; + } + var pair = f(17); + console.assert(pair[0] === 42); + console.assert(pair[1] === 17); + +然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 `arguments.callee`。 + + function foo() { + arguments.callee; // 使用这个函数对象 + arguments.callee.caller; // 以及这个函数对象的调用者 + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // 通常情况会作为内联函数... + } + } + +上面代码中,`foo` 不再是一个单纯的内联函数 [inlining][1](**[译者注][30]**:这里指的是解析器可以做内联处理), +因为它需要知道它自己和它的调用者。 +这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。 + +因此**强烈**建议大家**不要**使用 `arguments.callee` 和它的属性。 + +> **ES5 提示:** 在严格模式下,`arguments.callee` 会报错 `TypeError`,因为它已经被废除了。 + +[1]: http://en.wikipedia.org/wiki/Inlining +[2]: https://developer.mozilla.org/en/JavaScript/Strict_mode +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/function/closures.md b/external/JavaScript-Garden/doc/zh/function/closures.md new file mode 100755 index 0000000..df474a6 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/closures.md @@ -0,0 +1,86 @@ +##闭包和引用 + +闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域**总是**能够访问外部作用域中的变量。 +因为 [函数](#function.scopes) 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。 + +> 译者注:ES2015 中增加了块级作用域。 + +###模拟私有变量 + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +这里,`Counter` 函数返回两个闭包,函数 `increment` 和函数 `get`。 这两个函数都维持着 +对外部作用域 `Counter` 的引用,因此总可以访问此作用域内定义的变量 `count`。 + +###为什么不可以在外部访问私有变量 + +因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 `count` 变量。 +唯一的途径就是通过那两个闭包。 + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +上面的代码**不会**改变定义在 `Counter` 作用域中的 `count` 变量的值,因为 `foo.hack` 没有 +定义在那个**作用域**内。它将会创建或者覆盖*全局*变量 `count`。 + +###循环中的闭包 + +一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号 + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +上面的代码不会输出数字 `0` 到 `9`,而是会输出数字 `10` 十次。 + +当 `console.log` 被调用的时候,*匿名*函数保持对外部变量 `i` 的引用,此时 `for`循环已经结束, `i` 的值被修改成了 `10`。 + +为了得到想要的结果,需要在每次循环中创建变量 `i` 的**拷贝**。 + +###避免引用错误 + +为了正确的获得循环序号,最好使用 [匿名包装器](#function.scopes)(**[译者注][30]:**其实就是我们通常说的自执行匿名函数)。 + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +外部的匿名函数会立即执行,并把 `i` 作为它的参数,此时函数内 `e` 变量就拥有了 `i` 的一个拷贝。 + +当传递给 `setTimeout` 的匿名函数执行时,它就拥有了对 `e` 的引用,而这个值是**不会**被循环改变的。 + +有另一个方法完成同样的工作,那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。 + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + }; + })(i), 1000); + } + + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/function/constructors.md b/external/JavaScript-Garden/doc/zh/function/constructors.md new file mode 100755 index 0000000..137dc38 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/constructors.md @@ -0,0 +1,136 @@ +##构造函数 + +JavaScript 中的构造函数和其它语言中的构造函数是不同的。 +通过 `new` 关键字方式调用的函数都被认为是构造函数。 + +在构造函数内部 - 也就是被调用的函数内 - `this` 指向新创建的对象 `Object`。 +这个**新创建**的对象的 [`prototype`](#object.prototype) 被指向到构造函数的 `prototype`。 + +如果被调用的函数没有显式的 `return` 表达式,则隐式的会返回 `this` 对象 - 也就是新创建的对象。 + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +上面代码把 `Foo` 作为构造函数调用,并设置新创建对象的 `prototype` 为 `Foo.prototype`。 + +显式的 `return` 表达式将会影响返回结果,但**仅限**于返回的是一个对象。 + + function Bar() { + return 2; + } + new Bar(); // 返回新创建的对象 + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // 返回的对象 + + +**[译者注][30]:**`new Bar()` 返回的是新创建的对象,而不是数字的字面值 2。 +因此 `new Bar().constructor === Bar`,但是如果返回的是数字对象,结果就不同了,如下所示 + + function Bar() { + return new Number(2); + } + new Bar().constructor === Number + + +**[译者注][30]:**这里得到的 `new Test()`是函数返回的对象,而不是通过`new`关键字新创建的对象,因此: + + (new Test()).value === undefined + (new Test()).foo === 1 + + +如果 `new` 被遗漏了,则函数**不会**返回新创建的对象。 + + function Foo() { + this.bla = 1; // 获取设置全局参数 + } + Foo(); // undefined + +虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 [`this`](#function.this) 的工作原理, +这里的 `this` 指向*全局对象*。 + +### 工厂模式 + +为了不使用 `new` 关键字,构造函数必须显式的返回一个值。 + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +上面两种对 `Bar` 函数的调用返回的值完全相同,一个新创建的拥有 `method` 属性的对象被返回, +其实这里创建了一个[闭包](#function.closures)。 + +还需要注意, `new Bar()` 并**不会**改变返回对象的原型(**[译者注][30]:**也就是返回对象的原型不会指向 `Bar.prototype`)。 +因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 `Bar` 没有把这个新对象返回([译者注][30]:而是返回了一个包含 `method` 属性的自定义对象)。 + +在上面的例子中,使用或者不使用 `new` 关键字没有功能性的区别。 + +**[译者注][30]:**上面两种方式创建的对象不能访问 `Bar` 原型链上的属性,如下所示: + + var bar1 = new Bar(); + typeof(bar1.method); // "function" + typeof(bar1.foo); // "undefined" + + var bar2 = Bar(); + typeof(bar2.method); // "function" + typeof(bar2.foo); // "undefined" + +###通过工厂模式创建新对象 + +我们常听到的一条忠告是**不要**使用 `new` 关键字来调用函数,因为如果忘记使用它就会导致错误。 + +为了创建新对象,我们可以创建一个工厂方法,并且在方法内构造一个新对象。 + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +虽然上面的方式比起 `new` 的调用方式不容易出错,并且可以充分利用[私有变量](#function.closures)带来的便利, +但是随之而来的是一些不好的地方。 + + + 1. 会占用更多的内存,因为新创建的对象**不能**共享原型上的方法。 + 2. 为了实现继承,工厂方法需要从另外一个对象拷贝所有属性,或者把一个对象作为新创建对象的原型。 + 3. 放弃原型链仅仅是因为防止遗漏 `new` 带来的问题,这似乎和语言本身的思想相违背。 + +###总结 + +虽然遗漏 `new` 关键字可能会导致问题,但这并**不是**放弃使用原型链的借口。 +最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并**坚持**下去才是最重要的。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/function/general.md b/external/JavaScript-Garden/doc/zh/function/general.md new file mode 100755 index 0000000..0441490 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/general.md @@ -0,0 +1,44 @@ +##函数声明与表达式 + +函数是 JavaScript 中的一等对象,这意味着可以把函数像其它值一样传递。 +一个常见的用法是把*匿名函数*作为回调函数传递到异步函数中。 + +###函数声明 + + function foo() {} + +上面的方法会在执行前被 [解析(hoisted)](#function.scopes),因此它存在于当前上下文的*任意*一个地方, +即使在函数定义体的上面被调用也是对的。 + + foo(); // 正常运行,因为 foo 在代码运行前已经被创建 + function foo() {} + +### 函数赋值表达式 + + var foo = function() {}; + +这个例子把一个*匿名*的函数赋值给变量 `foo`。 + + foo; // 'undefined' + foo(); // 出错:TypeError + var foo = function() {}; + +由于 `var` 定义了一个声明语句,对变量 `foo` 的解析是在代码运行之前,因此 `foo` 变量在代码运行时已经被定义过了。 + +但是由于赋值语句只在运行时执行,因此在相应代码执行之前, `foo` 的值缺省为 [undefined](#core.undefined)。 + +###命名函数的赋值表达式 + +另外一个特殊的情况是将命名函数赋值给一个变量。 + + var foo = function bar() { + bar(); // 正常运行 + } + bar(); // 出错:ReferenceError + +`bar` 函数声明外是不可见的,这是因为我们已经把函数赋值给了 `foo`; +然而在 `bar` 内部依然可见。这是由于 JavaScript 的 [命名处理](#function.scopes) 所致, +函数名在函数内*总是*可见的。 +> **注意: **在 IE8 及 IE8 以下版本浏览器 bar 在外部也是可见的,是因为浏览器对命名函数赋值表达式进行了错误的解析, +> 解析成两个函数 `foo` 和 `bar` + diff --git a/external/JavaScript-Garden/doc/zh/function/scopes.md b/external/JavaScript-Garden/doc/zh/function/scopes.md new file mode 100755 index 0000000..32fe937 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/scopes.md @@ -0,0 +1,220 @@ +##作用域与命名空间 + +尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +而仅仅支持 *函数作用域*。 + + function test() { // 一个作用域 + for(var i = 0; i < 10; i++) { // 不是一个作用域 + // count + } + console.log(i); // 10 + } + +> **注意:** 如果不是在赋值语句中,而是在 return 表达式或者函数参数中,`{...}` 将会作为代码段解析, +> 而不是作为对象的字面语法解析。如果考虑到 [自动分号插入](#core.semicolon),这可能会导致一些不易察觉的错误。 + +**[译者注][30]:**如果 `return` 对象的左括号和 `return` 不在一行上就会出错。 + + // 译者注:下面输出 undefined + function add(a, b) { + return + a + b; + } + console.log(add(1, 2)); + +JavaScript 中没有显式的命名空间定义,这就意味着所有对象都定义在一个*全局共享*的命名空间下面。 + +每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 +如果到达全局作用域但是这个变量仍未找到,则会抛出 `ReferenceError` 异常。 + +###隐式的全局变量 + + // 脚本 A + foo = '42'; + + // 脚本 B + var foo = '42' + +上面两段脚本效果**不同**。脚本 A 在*全局*作用域内定义了变量 `foo`,而脚本 B 在*当前*作用域内定义变量 `foo`。 + +再次强调,上面的效果**完全不同**,不使用 `var` 声明变量将会导致隐式的全局变量产生。 + + // 全局作用域 + var foo = 42; + function test() { + // 局部作用域 + foo = 21; + } + test(); + foo; // 21 + +在函数 `test` 内不使用 `var` 关键字声明 `foo` 变量将会覆盖外部的同名变量。 +起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 `var` 声明变量将会带来难以跟踪的 BUG。 + + // 全局作用域 + var items = [/* 数组 */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // subLoop 函数作用域 + for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 + // 干活 + } + } + +外部循环在第一次调用 `subLoop` 之后就会终止,因为 `subLoop` 覆盖了全局变量 `i`。 +在第二个 `for` 循环中使用 `var` 声明变量可以避免这种错误。 +声明变量时**绝对不要**遗漏 `var` 关键字,除非这就是*期望*的影响外部作用域的行为。 + +###局部变量 + +JavaScript 中局部变量只可能通过两种方式声明,一个是作为[函数](#function)参数,另一个是通过 `var` 关键字声明。 + + // 全局变量 + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // 函数 test 内的局部作用域 + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` 和 `i` 是函数 `test` 内的局部变量,而对 `bar` 的赋值将会覆盖全局作用域内的同名变量。 + +###变量声明提升(Hoisting) + +JavaScript 会**提升**变量声明。这意味着 `var` 表达式和 `function` 声明都将会被提升到当前作用域的顶部。 + + 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]; + } + } + +上面代码在运行之前将会被转化。JavaScript 将会把 `var` 表达式和 `function` 声明提升到当前作用域的顶部。 + + // var 表达式被移动到这里 + var bar, someValue; // 缺省值是 'undefined' + + // 函数声明也会提升 + function test(data) { + var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部 + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // 出错:TypeError,因为 bar 依然是 'undefined' + someValue = 42; // 赋值语句不会被提升规则(hoisting)影响 + bar = function() {}; + + test(); + +没有块级作用域不仅导致 `var` 表达式被从循环内移到外部,而且使一些 `if` 表达式更难看懂。 + +在原来代码中,`if` 表达式看起来修改了*全局变量* `goo`,实际上在提升规则被应用后,却是在修改*局部变量*。 + +如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 `ReferenceError`。 + + // 检查 SomeImportantThing 是否已经被初始化 + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +实际上,上面的代码正常运行,因为 `var` 表达式会被提升到*全局作用域*的顶部。 + + var SomeImportantThing; + + // 其它一些代码,可能会初始化 SomeImportantThing,也可能不会 + + // 检查是否已经被初始化 + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + + +**[译者注][30]:**在 Nettuts+ 网站有一篇介绍 hoisting 的[文章][1],其中的代码很有启发性。 + + // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则 + var myvar = 'my value'; + + (function() { + alert(myvar); // undefined + var myvar = 'local value'; + })(); + + +###名称解析顺序 + +JavaScript 中的所有作用域,包括*全局作用域*,都有一个特别的名称 [`this`](#function.this) 指向当前对象。 + +函数作用域内也有默认的变量 [`arguments`](#function.arguments),其中包含了传递到函数中的参数。 + +比如,当访问函数内的 `foo` 变量时,JavaScript 会按照下面顺序查找: + + 1. 当前作用域内是否有 `var foo` 的定义。 + 2. 函数形式参数是否有使用 `foo` 名称的。 + 3. 函数自身是否叫做 `foo`。 + 4. 回溯到上一级作用域,然后从 **#1** 重新开始。 + +> **注意:** 自定义 `arguments` 参数将会阻止原生的 `arguments` 对象的创建。 + +###命名空间 + +只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 *匿名包装器* 轻松解决。 + + (function() { + // 函数创建一个命名空间 + + window.foo = function() { + // 对外公开的函数,创建了闭包 + }; + + })(); // 立即执行此匿名函数 + +匿名函数被认为是 [表达式](#function);因此为了可调用性,它们首先会被执行。 + + ( // 小括号内的函数首先被执行 + function() {} + ) // 并且返回函数对象 + () // 调用上面的执行结果,也就是函数对象 + +有一些其他的调用函数表达式的方法,比如下面的两种方式语法不同,但是效果一模一样。 + + // 另外两种方式 + +function(){}(); + (function(){}()); + +###结论 + +推荐使用*匿名包装器*(**[译者注][30]:**也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +而且有利于程序的模块化。 + +另外,使用全局变量被认为是**不好的习惯**。这样的代码容易产生错误并且维护成本较高。 + +[1]: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/ +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/function/this.md b/external/JavaScript-Garden/doc/zh/function/this.md new file mode 100755 index 0000000..967c9df --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/function/this.md @@ -0,0 +1,102 @@ +##`this` 的工作原理 + +JavaScript 有一套完全不同于其它语言的对 `this` 的处理机制。 +在**五**种不同的情况下 ,`this` 指向的各不相同。 + +###全局范围内 + + this; + +当在全部范围内使用 `this`,它将会指向*全局*对象。 + +> **[译者注][30]:**浏览器中运行的 JavaScript 脚本,这个全局对象是 `window`; +> 在 nodejs 环境中运行的 Javascript 脚本,这个全局对象是 `global`。 + +###函数调用 + + foo(); + +这里 `this` 也会指向*全局*对象。 + +> **ES5 注意:** 在严格模式下(strict mode),不存在全局变量。 +> 这种情况下 `this` 将会是 `undefined`。 + +###方法调用 + + test.foo(); + +这个例子中,`this` 指向 `test` 对象。 + +###调用构造函数 + + new foo(); + +如果函数倾向于和 `new` 关键词一块使用,则我们称这个函数是 [构造函数](#function.constructors)。 +在函数内部,`this` 指向*新创建*的对象。 + +###显式的设置 `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // 数组将会被扩展,如下所示 + foo.call(bar, 1, 2, 3); // 传递到 foo 的参数是:a = 1, b = 2, c = 3 + +当使用 `Function.prototype` 上的 `call` 或者 `apply` 方法时,函数内的 `this` 将会被 +**显式设置**为函数调用的第一个参数。 + +因此*函数调用*的规则在上例中已经不适用了,在`foo` 函数内 `this` 被设置成了 `bar`。 + +> **注意:** 在对象的字面声明语法中,`this` **不能**用来指向对象本身。 +> 因此 `var obj = {me: this}` 中的 `me` 不会指向 `obj`,因为 `this` 只可能出现在上述的五种情况中。 +> **[译者注][30]:**这个例子中,如果是在浏览器中运行,`obj.me` 等于 `window` 对象。 + +###常见误解 + +尽管大部分的情况都说的过去,不过第一个规则(**[译者注][30]:**这里指的应该是第二个规则,也就是直接调用函数时,`this` 指向全局对象) +被认为是 JavaScript 语言另一个错误设计的地方,因为它**从来**就没有实际的用途。 + + Foo.method = function() { + function test() { + // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象) + } + test(); + }; + +一个常见的误解是 `test` 中的 `this` 将会指向 `Foo` 对象,实际上**不是**这样子的。 + +为了在 `test` 中获取对 `Foo` 对象的引用,我们需要在 `method` 函数内部创建一个局部变量指向 `Foo` 对象。 + + Foo.method = function() { + var that = this; + function test() { + // 使用 that 来指向 Foo 对象 + } + test(); + }; + +`that` 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 `this` 对象。 +在 [闭包](#function.closures) 一节,我们可以看到 `that` 可以作为参数传递。 + +###方法的赋值表达式 + +另一个看起来奇怪的地方是函数别名,也就是将一个方法**赋值**给一个变量。 + + var test = someObject.methodTest; + test(); + +上例中,`test` 就像一个普通的函数被调用;因此,函数内的 `this` 将不再被指向到 `someObject` 对象。 + +虽然 `this` 的晚绑定特性似乎并不友好,但这确实是[基于原型继承](#object.prototype)赖以生存的土壤。 + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +当 `method` 被调用时,`this` 将会指向 `Bar` 的实例对象。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/index.json b/external/JavaScript-Garden/doc/zh/index.json new file mode 100755 index 0000000..be3d552 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/index.json @@ -0,0 +1,72 @@ +{ + "title": "JavaScript 秘密花园", + "langTitle": "JavaScript Garden 中文翻译", + "description": "JavaScript 语言最古怪用法的文档集合", + "sections": [ + { + "title": "简介", + "dir": "intro", + "articles": [ + "authors", + "contributors", + "license" + ] + }, + { + "title": "对象", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "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" + ] + }, + { + "title": "核心", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon" + ] + }, + { + "title": "其它", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/zh/intro/authors.md b/external/JavaScript-Garden/doc/zh/intro/authors.md new file mode 100755 index 0000000..e923ba8 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/intro/authors.md @@ -0,0 +1,7 @@ +##关于作者 + +这篇文章的作者是两位 [Stack Overflow][1] 用户, [伊沃·韦特泽尔 Ivo Wetzel][2](写作) 和 [张易江 Zhang Yi Jiang][3](设计)。 + +[1]: http://stackoverflow.com/ +[2]: http://stackoverflow.com/users/170224/ivo-wetzel +[3]: http://stackoverflow.com/users/313758/yi-jiang diff --git a/external/JavaScript-Garden/doc/zh/intro/contributors.md b/external/JavaScript-Garden/doc/zh/intro/contributors.md new file mode 100755 index 0000000..a704d4a --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/intro/contributors.md @@ -0,0 +1,13 @@ +##贡献者 + +- [贡献者](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +##中文翻译 + - [三生石上][29] + +此中文翻译由[三生石上][29]独立完成,[博客园][30]首发,转载请注明出处。 + +[1]: https://github.com/caio +[2]: https://github.com/blixt +[29]: http://sanshi.me/ +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/intro/index.md b/external/JavaScript-Garden/doc/zh/intro/index.md new file mode 100755 index 0000000..f141e17 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/intro/index.md @@ -0,0 +1,14 @@ +##简介 + +**JavaScript 秘密花园**是一个不断更新,主要关心 JavaScript 一些古怪用法的文档。 +对于如何避免常见的错误,难以发现的问题,以及性能问题和不好的实践给出建议, +初学者可以籍此深入了解 JavaScript 的语言特性。 + +JavaScript 秘密花园**不是**用来教你 JavaScript。为了更好的理解这篇文章的内容, +你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习[向导][1]。 + +> **译者注:** 文中提到的 ES5 是 ECMAScript 5 的简写,是 ECMAScript 标准语言的下一版本,正在开发中。 +JavaScript 是此标准语言的一个方言。 + +[1]: https://developer.mozilla.org/en/JavaScript/Guide + diff --git a/external/JavaScript-Garden/doc/zh/intro/license.md b/external/JavaScript-Garden/doc/zh/intro/license.md new file mode 100755 index 0000000..057dd8a --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/intro/license.md @@ -0,0 +1,11 @@ +##许可 + +JavaScript 秘密花园在 [MIT license][1] 许可协议下发布,并存放在 [GitHub][2] 开源社区。 +如果你发现错误,请[新建一个任务单][3]或者发一个抓取请求(Pull Request)。 +你也可以在 Stack Overflow 的 [JavaScript 聊天室][4]找到我们。 + +[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 + diff --git a/external/JavaScript-Garden/doc/zh/object/forinloop.md b/external/JavaScript-Garden/doc/zh/object/forinloop.md new file mode 100755 index 0000000..16f9faa --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/object/forinloop.md @@ -0,0 +1,40 @@ +##`for in` 循环 + +和 `in` 操作符一样,`for in` 循环同样在查找对象属性时遍历原型链上的所有属性。 + +> **注意:** `for in` 循环**不会**遍历那些 `enumerable` 设置为 `false` 的属性;比如数组的 `length` 属性。 + + // 修改 Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // 输出两个属性:bar 和 moo + } + +由于不可能改变 `for in` 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, +这可以通过 `Object.prototype` 原型上的 [`hasOwnProperty`](#object.hasownproperty) 函数来完成。 + +> **注意:** 由于 `for in` 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能。 + +###使用 `hasOwnProperty` 过滤 + + // foo 变量是上例中的 + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +这个版本的代码是唯一正确的写法。由于我们使用了 `hasOwnProperty`,所以这次**只**输出 `moo`。 +如果不使用 `hasOwnProperty`,则这段代码在原生对象原型(比如 `Object.prototype`)被扩展时可能会出错。 + +一个广泛使用的类库 [Prototype][1] 就扩展了原生的 JavaScript 对象。 +因此,当这个类库被包含在页面中时,不使用 `hasOwnProperty` 过滤的 `for in` 循环难免会出问题。 + +###总结 + +推荐**总是**使用 `hasOwnProperty`。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。 + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/zh/object/general.md b/external/JavaScript-Garden/doc/zh/object/general.md new file mode 100755 index 0000000..3ada17f --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/object/general.md @@ -0,0 +1,93 @@ +##对象使用和属性 + +JavaScript 中所有变量都可以当作对象使用,除了两个例外 [`null`](#core.undefined) 和 [`undefined`](#core.undefined)。 + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +一个常见的误解是数字的字面值(literal)不能当作对象使用。这是因为 JavaScript 解析器的一个错误, +它试图将*点操作符*解析为浮点数字面值的一部分。 + + 2.toString(); // 出错:SyntaxError + +有很多变通方法可以让数字的字面值看起来像对象。 + + 2..toString(); // 第二个点号可以正常解析 + 2 .toString(); // 注意点号前面的空格 + (2).toString(); // 2先被计算 + +###对象作为数据类型 + +JavaScript 的对象可以作为[*哈希表*][1]使用,主要用来保存命名的键与值的对应关系。 + +使用对象的字面语法 - `{}` - 可以创建一个简单对象。这个新创建的对象从 `Object.prototype` +[继承](#object.prototype)下来,没有任何[自定义属性](#object.hasownproperty)。 + + var foo = {}; // 一个空对象 + + // 一个新对象,拥有一个值为12的自定义属性'test' + var bar = {test: 12}; + +### 访问属性 + +有两种方式来访问对象的属性,点操作符和中括号操作符。 + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +两种语法是等价的,但是中括号操作符在下面两种情况下依然有效 + - 动态设置属性 + - 属性名不是一个有效的变量名(**[译者注][30]:**比如属性名中包含空格,或者属性名是 JS 的关键词) + +> **[译者注][30]:**在 [JSLint][2] 语法检测工具中,点操作符是推荐做法。 + +###删除属性 + +删除属性的唯一方法是使用 `delete` 操作符;设置属性为 `undefined` 或者 `null` 并不能真正的删除属性, +而**仅仅**是移除了属性和值的关联。 + + 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]); + } + } + +上面的输出结果有 `bar undefined` 和 `foo null` - 只有 `baz` 被真正的删除了,所以从输出结果中消失。 + +###属性名的语法 + + var test = { + 'case': 'I am a keyword so I must be notated as a string', + delete: 'I am a keyword too so me' // 出错:SyntaxError + }; + +对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计, +上面的第二种声明方式在 ECMAScript 5 之前会抛出 `SyntaxError` 的错误。 + +这个错误的原因是 `delete` 是 JavaScript 语言的一个*关键词*;因此为了在更低版本的 JavaScript 引擎下也能正常运行, +必须使用*字符串字面值*声明方式。 + +[1]: http://en.wikipedia.org/wiki/Hashmap +[2]: http://www.jslint.com/ +[30]: http://cnblogs.com/sanshi/ + diff --git a/external/JavaScript-Garden/doc/zh/object/hasownproperty.md b/external/JavaScript-Garden/doc/zh/object/hasownproperty.md new file mode 100755 index 0000000..c9b3fc2 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/object/hasownproperty.md @@ -0,0 +1,46 @@ +##`hasOwnProperty` 函数 + +为了判断一个对象是否包含*自定义*属性而*不是*[原型链](#object.prototype)上的属性, +我们需要使用继承自 `Object.prototype` 的 `hasOwnProperty` 方法。 + +> **注意:** 通过判断一个属性是否 `undefined` 是**不够**的。 +> 因为一个属性可能确实存在,只不过它的值被设置为 `undefined`。 + +`hasOwnProperty` 是 JavaScript 中唯一一个处理属性但是**不**查找原型链的函数。 + + // 修改 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 + +只有 `hasOwnProperty` 可以给出正确和期望的结果。可以查看 [`for in` 循环](#object.forinloop) +章节来获取关于在迭代遍历对象属性的时候,何时使用 `hasOwnProperty` 的更多信息。 + +###`hasOwnProperty` 作为属性 + +JavaScript **不会**保护 `hasOwnProperty` 被非法占用,因此如果一个对象碰巧存在这个属性, +就需要使用*外部*的 `hasOwnProperty` 函数来获取正确的结果。 + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // 总是返回 false + + // 使用其它对象的 hasOwnProperty,并将其上下文设置为 foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + +###结论 + +当检查对象上某个属性是否存在时,`hasOwnProperty` 是**唯一**可用的方法。 +同时在使用 [`for in` loop](#object.forinloop) 遍历对象时,推荐**总是**使用 `hasOwnProperty` 方法, +这将会避免[原型](#object.prototype)对象扩展带来的干扰。 + diff --git a/external/JavaScript-Garden/doc/zh/object/prototype.md b/external/JavaScript-Garden/doc/zh/object/prototype.md new file mode 100755 index 0000000..25a1d89 --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/object/prototype.md @@ -0,0 +1,96 @@ +##原型 + +JavaScript 不包含传统的类继承模型,而是使用 *prototype* 原型模型。 + +虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 +例如,很容易通过原型继承实现传统的类继承模型,但是反过来,通过传统的类继承模型来实现原型继承模型就困难得多。 +(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.) + +由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的。 + +第一个不同之处在于 JavaScript 使用*原型链*的继承方式。 + +> **注意:** 简单的使用 `Bar.prototype = Foo.prototype` 将会导致两个对象共享**相同**的原型。 +> 因此,改变任意一个对象的原型都会影响到另一个对象的原型,在大多数情况下这不是希望的结果。 + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // 设置 Bar 的 prototype 属性为 Foo 的实例对象 + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // 修正 Bar.prototype.constructor 为 Bar 本身 + Bar.prototype.constructor = Bar; + + var test = new Bar(); // 创建Bar的一个新实例 + + // 原型链 + test [Bar的实例] + Bar.prototype [Foo的实例] + { foo: 'Hello World', value: 42 } + Foo.prototype + {method: ...}; + Object.prototype + {toString: ... /* etc. */}; + +上面的例子中,`test` 对象从 `Bar.prototype` 和 `Foo.prototype` 继承下来;因此, +它能访问 `Foo` 的原型方法 `method`。同时,它也能够访问**那个**定义在原型上的 `Foo` 实例属性 `value`。 +需要注意的是 `new Bar()` **不会**创造出一个新的 `Foo` 实例,而是 +重复使用它原型上的那个实例;因此,所有的 `Bar` 实例都会共享**相同**的 `value` 属性。 + +> **注意:** **不要**使用 `Bar.prototype = Foo`,因为这不会指向 `Foo` 的原型,而是指向函数 `Foo`。 +> 因此原型链将会回溯到 `Function.prototype` 而不是 `Foo.prototype`,因此 `method` 将不会在 Bar 的原型链上。 + +###属性查找 + +当查找一个对象的属性时,JavaScript 会**向上**遍历原型链,直到找到给定名称的属性为止。 + +到查找到达原型链的顶部 - 也就是 `Object.prototype` - 但是仍然没有找到指定的属性,就会返回 [undefined](#core.undefined)。 + +###原型属性 + +当原型属性用来创建原型链时,可以把**任何**类型的值赋给它(prototype)。 +然而将原子类型(primitives)赋给 prototype 的操作将会被忽略。 + + function Foo() {} + Foo.prototype = 1; // 无效 + +而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。 + +###性能 + +如果一个属性在原型链的上端,则对于查找时间将带来不利影响。注意,试图获取一个不存在的属性将会遍历整个原型链。 + +并且,当使用 [`for in`](#object.forinloop) 循环遍历对象的属性时,原型链上的**所有**属性都将被访问。 + +###扩展内置类型的原型 + +扩展 `Object.prototype` 或者其他内置类型的原型对象,作为一个错误特性,经常被使用。 + +这种技术被称之为 [monkey patching][1] 并且会破坏*封装*。虽然它被广泛的应用到一些 JavaScript 类库中比如 [Prototype][2], +但是我仍然不赞同为内置类型添加一些*非标准*的函数。 + +扩展内置类型的**唯一**理由是为了和新的 JavaScript 保持一致,比如 [`Array.forEach`][3]。 + +> **[译者注][30]:**这是编程领域常用的一种方式,称之为 [Backport][5],也就是将新的补丁添加到老版本中。 + +###总结 + +在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员**必修**的功课。 +要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 +更进一步,绝对**不要**扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。 + + +[1]: http://en.wikipedia.org/wiki/Monkey_patch +[2]: http://prototypejs.org/ +[3]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach +[5]: http://en.wikipedia.org/wiki/Backport +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/other/timeouts.md b/external/JavaScript-Garden/doc/zh/other/timeouts.md new file mode 100755 index 0000000..4b2cc5d --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/other/timeouts.md @@ -0,0 +1,132 @@ +###`setTimeout` 和 `setInterval` + +由于 JavaScript 是异步的,可以使用 `setTimeout` 和 `setInterval` 来计划执行函数。 + +> **注意:** 定时处理**不是** ECMAScript 的标准,它们在 [DOM (文档对象模型)][1] 被实现。 + + function foo() {} + var id = setTimeout(foo, 1000); // 返回一个大于零的数字 + +当 `setTimeout` 被调用时,它会返回一个 ID 标识并且计划在将来**大约** 1000 毫秒后调用 `foo` 函数。 +`foo` 函数只会被执行**一次**。 + +基于 JavaScript 引擎的计时策略,以及本质上的单线程运行方式,所以其它代码的运行可能会阻塞此线程。 +因此**没法确保**函数会在 `setTimeout` 指定的时刻被调用。 + +作为第一个参数的函数将会在*全局作用域*中执行,因此函数内的 [`this`](#function.this) 将会指向这个全局对象。 + + function Foo() { + this.value = 42; + this.method = function() { + // this 指向全局对象 + console.log(this.value); // 输出:undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + + +> **注意:** `setTimeout` 的第一个参数是**函数对象**,一个常犯的错误是这样的 `setTimeout(foo(), 1000)`, +> 这里回调函数是 `foo` 的**返回值**,而**不是**`foo`本身。 +> 大部分情况下,这是一个潜在的错误,因为如果函数返回 `undefined`,`setTimeout` 也**不会**报错。 + +###`setInterval` 的堆调用 + +`setTimeout` 只会执行回调函数一次,不过 `setInterval` - 正如名字建议的 - 会每隔 `X` 毫秒执行函数一次。 +但是却不鼓励使用这个函数。 + +当回调函数的执行被阻塞时,`setInterval` 仍然会发布更多的回调指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。 + + function foo(){ + // 阻塞执行 1 秒 + } + setInterval(foo, 100); + +上面代码中,`foo` 会执行一次随后被阻塞了一秒钟。 + +在 `foo` 被阻塞的时候,`setInterval` 仍然在组织将来对回调函数的调用。 +因此,当第一次 `foo` 函数调用结束时,已经有 **10** 次函数调用在等待执行。 + +###处理可能的阻塞调用 + +最简单也是最容易控制的方案,是在回调函数内部使用 `setTimeout` 函数。 + + function foo(){ + // 阻塞执行 1 秒 + setTimeout(foo, 100); + } + foo(); + +这样不仅封装了 `setTimeout` 回调函数,而且阻止了调用指令的堆积,可以有更多的控制。 +`foo` 函数现在可以控制是否继续执行还是终止执行。 + + +###手工清空定时器 + +可以通过将定时时产生的 ID 标识传递给 `clearTimeout` 或者 `clearInterval` 函数来清除定时, +至于使用哪个函数取决于调用的时候使用的是 `setTimeout` 还是 `setInterval`。 + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +###清除所有定时器 + +由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。 + + // 清空"所有"的定时器 + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +可能还有些定时器不会在上面代码中被清除(**[译者注][30]:**如果定时器调用时返回的 ID 值大于 1000), +因此我们可以事先保存所有的定时器 ID,然后一把清除。 + +###隐藏使用 `eval` + +`setTimeout` 和 `setInterval` 也接受第一个参数为字符串的情况。 +这个特性**绝对**不要使用,因为它在内部使用了 `eval`。 + +> **注意:** 由于定时器函数不是 ECMAScript 的标准,如何解析字符串参数在不同的 JavaScript 引擎实现中可能不同。 +> 事实上,微软的 JScript 会使用 `Function` 构造函数来代替 `eval` 的使用。 + + function foo() { + // 将会被调用 + } + + function bar() { + function foo() { + // 不会被调用 + } + setTimeout('foo()', 1000); + } + bar(); + +由于 `eval` 在这种情况下不是被[直接](#core.eval)调用,因此传递到 `setTimeout` 的字符串会到*全局作用域*中执行; +因此,上面的回调函数使用的不是定义在 `bar` 作用域中的局部变量 `foo`。 + +建议**不要**在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。 + + function foo(a, b, c) {} + + // 不要这样做 + setTimeout('foo(1,2, 3)', 1000) + + // 可以使用匿名函数完成相同功能 + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **注意:** 虽然也可以使用这样的语法 `setTimeout(foo, 1000, 1, 2, 3)`, +> 但是不推荐这么做,因为在使用对象的[属性方法](#function.this)时可能会出错。 +>(**译者注:**这里说的是属性方法内,`this` 的指向错误) + +###结论 + +**绝对不要**使用字符串作为 `setTimeout` 或者 `setInterval` 的第一个参数, +这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个*匿名函数*,在函数内执行真实的回调函数。 + +另外,应该避免使用 `setInterval`,因为它的定时执行不会被 JavaScript 阻塞。 + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model +[30]: http://cnblogs.com/sanshi/ + diff --git a/external/JavaScript-Garden/doc/zh/types/casting.md b/external/JavaScript-Garden/doc/zh/types/casting.md new file mode 100755 index 0000000..df7383b --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/types/casting.md @@ -0,0 +1,74 @@ +##类型转换 + +JavaScript 是*弱类型*语言,所以会在**任何**可能的情况下应用*强制类型转换*。 + + // 下面的比较结果是:true + new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字 + + 10 == '10'; // 字符串被转换为数字 + 10 == '+10 '; // 同上 + 10 == '010'; // 同上 + isNaN(null) == false; // null 被转换为数字 0 + // 0 当然不是一个 NaN(译者注:否定之否定) + + // 下面的比较结果是:false + 10 == 010; + 10 == '-10'; + +> **ES5 提示:** 以 `0` 开头的数字字面值会被作为八进制数字解析。 +> 而在 ECMAScript 5 严格模式下,这个特性被**移除**了。 + +为了避免上面复杂的强制类型转换,**强烈**推荐使用[严格的等于操作符](#types.equality)。 +虽然这可以避免大部分的问题,但 JavaScript 的弱类型系统仍然会导致一些其它问题。 + +###内置类型的构造函数 + +内置类型(比如 `Number` 和 `String`)的构造函数在被调用时,使用或者不使用 `new` 的结果完全不同。 + + new Number(10) === 10; // False, 对象与数字的比较 + Number(10) === 10; // True, 数字与数字的比较 + new Number(10) + 0 === 10; // True, 由于隐式的类型转换 + +使用内置类型 `Number` 作为构造函数将会创建一个新的 `Number` 对象, +而在不使用 `new` 关键字的 `Number` 函数更像是一个数字转换器。 + +另外,在比较中引入对象的字面值将会导致更加复杂的强制类型转换。 + +最好的选择是把要比较的值**显式**的转换为三种可能的类型之一。 + +###转换为字符串 + + '' + 10 === '10'; // true + +将一个值加上空字符串可以轻松转换为字符串类型。 + +###转换为数字 + + +'10' === 10; // true + +使用**一元**的加号操作符,可以把字符串转换为数字。 + +**[译者注][30]:**字符串转换为数字的常用方法: + + +'010' === 10 + Number('010') === 10 + parseInt('010', 10) === 10 // 用来转换为整数 + + +'010.2' === 10.2 + Number('010.2') === 10.2 + parseInt('010.2', 10) === 10 + + +###转换为布尔型 + +通过使用 **否** 操作符两次,可以把一个值转换为布尔型。 + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/types/equality.md b/external/JavaScript-Garden/doc/zh/types/equality.md new file mode 100755 index 0000000..006c02b --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/types/equality.md @@ -0,0 +1,63 @@ +##相等与比较 + +JavaScript 有两种方式判断两个值是否相等。 + +###等于操作符 + +等于操作符由两个等号组成:`==` + +JavaScript 是*弱类型*语言,这就意味着,等于操作符会为了比较两个值而进行**强制类型转换**。 + + "" == "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 + +上面的表格展示了强制类型转换,这也是使用 `==` 被广泛认为是不好编程习惯的主要原因, +由于它的复杂转换规则,会导致难以跟踪的问题。 + +此外,强制类型转换也会带来性能消耗,比如一个字符串为了和一个数字进行比较,必须事先被强制转换为数字。 + +###严格等于操作符 + +严格等于操作符由**三**个等号组成:`===` + +不像普通的等于操作符,严格等于操作符**不会**进行强制类型转换。 + + "" === "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 + +上面的结果更加清晰并有利于代码的分析。如果两个操作数类型不同就肯定不相等也有助于性能的提升。 + +###比较对象 + +虽然 `==` 和 `===` 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。 + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +这里等于操作符比较的**不是**值是否相等,而是是否属于同一个**身份**;也就是说,只有对象的同一个实例才被认为是相等的。 +这有点像 Python 中的 `is` 和 C 中的指针比较。 +> **注意:**为了更直观的看到`==`和`===`的区别,可以参见[JavaScript Equality Table](http://dorey.github.io/JavaScript-Equality-Table/) + +###结论 + +强烈推荐使用**严格等于操作符**。如果类型需要转换,应该在比较之前[显式](#types.casting)的转换, +而不是使用语言本身复杂的强制转换规则。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/types/instanceof.md b/external/JavaScript-Garden/doc/zh/types/instanceof.md new file mode 100755 index 0000000..e2783ef --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/types/instanceof.md @@ -0,0 +1,35 @@ +##`instanceof` 操作符 + +`instanceof` 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 +如果用来比较内置类型,将会和 [`typeof` 操作符](#types.typeof) 一样用处不大。 + +###比较自定义对象 + + function Foo() {} + function Bar() {} + Bar.prototype = new Foo(); + + new Bar() instanceof Bar; // true + new Bar() instanceof Foo; // true + + // 如果仅仅设置 Bar.prototype 为函数 Foo 本身,而不是 Foo 构造函数的一个实例 + Bar.prototype = Foo; + new Bar() instanceof Foo; // false + +###`instanceof` 比较内置类型 + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +有一点需要注意,`instanceof` 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, +因为它们的构造函数不会是同一个对象。 + +### 结论 + +`instanceof` 操作符应该**仅仅**用来比较来自同一个 JavaScript 上下文的自定义对象。 +正如 [`typeof`](#types.typeof) 操作符一样,任何其它的用法都应该是避免的。 + +[30]: http://cnblogs.com/sanshi/ diff --git a/external/JavaScript-Garden/doc/zh/types/typeof.md b/external/JavaScript-Garden/doc/zh/types/typeof.md new file mode 100755 index 0000000..fec6fcf --- /dev/null +++ b/external/JavaScript-Garden/doc/zh/types/typeof.md @@ -0,0 +1,97 @@ +##`typeof` 操作符 + +`typeof` 操作符(和 [`instanceof`](#types.instanceof) 一起)或许是 JavaScript 中最大的设计缺陷, +因为几乎不可能从它们那里得到想要的结果。 + +尽管 `instanceof` 还有一些极少数的应用场景,`typeof` 只有一个实际的应用(**[译者注][30]:**这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), +而这个应用却**不是**用来检查对象的类型。 + +> **注意:** 由于 `typeof` 也可以像函数的语法被调用,比如 `typeof(obj)`,但这并不是一个函数调用。 +> 那两个小括号只是用来计算一个表达式的值,这个返回值会作为 `typeof` 操作符的一个操作数。 +> 实际上**不存在**名为 `typeof` 的函数。 + +###JavaScript 类型表格 + + 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 + +上面表格中,*Type* 一列表示 `typeof` 操作符的运算结果。可以看到,这个值在大多数情况下都返回 "object"。 + +*Class* 一列表示对象的内部属性 `[[Class]]` 的值。 + +> **JavaScript 标准文档中定义:** `[[Class]]` 的值只可能是下面字符串中的一个: +> `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String`. + +为了获取对象的 `[[Class]]`,我们需要使用定义在 `Object.prototype` 上的方法 `toString`。 + +###对象的类定义 + +JavaScript 标准文档只给出了一种获取 `[[Class]]` 值的方法,那就是使用 `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 + +上面例子中,`Object.prototype.toString` 方法被调用,[this](#function.this) 被设置为了需要获取 `[[Class]]` 值的对象。 + +**[译者注][30]:**`Object.prototype.toString` 返回一种标准格式字符串,所以上例可以通过 `slice` 截取指定位置的字符串,如下所示: + + Object.prototype.toString.call([]) // "[object Array]" + Object.prototype.toString.call({}) // "[object Object]" + Object.prototype.toString.call(2) // "[object Number]" + +> **ES5 提示:** 在 ECMAScript 5 中,为了方便,对 `null` 和 `undefined` 调用 `Object.prototype.toString` 方法, +> 其返回值由 `Object` 变成了 `Null` 和 `Undefined`。 + + +**[译者注][30]:**这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示: + + // IE8 + Object.prototype.toString.call(null) // "[object Object]" + Object.prototype.toString.call(undefined) // "[object Object]" + + // Firefox 4 + Object.prototype.toString.call(null) // "[object Null]" + Object.prototype.toString.call(undefined) // "[object Undefined]" + + +###测试未定义变量 + + typeof foo !== 'undefined' + +上面代码会检测 `foo` 是否已经定义;如果没有定义而直接使用会导致 `ReferenceError` 的异常。 +这是 `typeof` 唯一有用的地方。 + + +###结论 + +为了检测一个对象的类型,强烈推荐使用 `Object.prototype.toString` 方法; +因为这是唯一一个可依赖的方式。正如上面表格所示,`typeof` 的一些返回值在标准文档中并未定义, +因此不同的引擎实现可能不同。 + +除非为了检测一个变量是否已经定义,我们应尽量避免使用 `typeof` 操作符。 + + +[30]: http://cnblogs.com/sanshi/ + + diff --git a/external/JavaScript-Garden/doc/zhtw/array/constructor.md b/external/JavaScript-Garden/doc/zhtw/array/constructor.md new file mode 100644 index 0000000..fd95249 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/array/constructor.md @@ -0,0 +1,28 @@ +## `Array` 的建構函式 + +`Array` 的建構函式在處理參數上一直有模糊的地帶,所以建議使用 `array`的字面語法來使用 - `[]` - 來新增一個的Array + + [1, 2, 3]; // 結果: [1, 2, 3] + new Array(1, 2, 3); // 結果: [1, 2, 3] + + [3]; // 結果: [3] + new Array(3); // 結果: [] + new Array('3') // 結果: ['3'] + +在上面的範例 `new Array(3)` 當只有一個參數傳入到 `Array` 的建構函數 +且那個參數是一個數字,建構函數會回傳空值 +但是 `Array` 長度的屬性會變成跟那個參數一樣(以此範例來看他回傳的長度為 3) +**注意** 只有他長度的屬性會被設定,整個 Array裡面的數值都不會初始化 + + var arr = new Array(3); + arr[1]; // undefined + 1 in arr; // false, 數值沒有被設定進去 + +被設定用來當做 `Array` 的長度只有少數情況使用 +先設定 `Array` 的長度可以用一下的範例來避免使用 `for loop` 的麻煩 + + new Array(count + 1).join(stringToRepeat); + +### 結語 + +`Array` 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性 diff --git a/external/JavaScript-Garden/doc/zhtw/array/general.md b/external/JavaScript-Garden/doc/zhtw/array/general.md new file mode 100644 index 0000000..1a59d72 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/array/general.md @@ -0,0 +1,46 @@ +## Array 迴圈和屬性 + +雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 +在 [`for in`](#object.forinloop) 的迴圈中。事實上有很多原因要避免使用 `for in` 在 Array 之中 + +> **注意:** Javascript Arrays **不是** *關連性 Arrays* +> 只有 [objects](#object.general) 來管理建值的相對應關係 +> Arrays 是**保持** 順序的,Objects **則沒有** + +因為 `for in` 迴圈會使用[`hasOwnProperty`](#object.hasownproperty),所以它會列舉所有在原型 Array 上的屬性,這會使得 Array 比原本的 `for` 迴圈慢上二十幾倍 + +### 迴圈 + +為了要達到最好的性能所以最好使用 `for` 迴圈來讀取一個 Array 裡面的數值。 + + var list = [1, 2, 3, 4, 5, ...... 100000000]; + for(var i = 0, l = list.length; i < l; i++) { + console.log(list[i]); + } + +在上面的例子中利用 `l = list.length` 來處理 Array 的長度問題。 + +雖然 `length` 屬性是屬於 Array 中其中一個屬性,但是他在每次循環還是有一定的性能消耗。 +近期 Javascript **可能**使用來解決在這上面的效率問題,但是在現在的引擎上還不一定有支援。 + +實際上,不使用暫存 Array 長度的方式比使用暫存的版本還要慢很多。 + +### `length` 的屬性 + +`length` 屬性中的 *getter* 直接回傳在 Array 之中的程度,而 *setter* 可以用來 **刪除** Array。 + + var foo = [1, 2, 3, 4, 5, 6]; + foo.length = 3; + foo; // [1, 2, 3] + + foo.length = 6; + foo.push(4); + foo; // [1, 2, 3, undefined, undefined, undefined, 4] + +在上面的例子可以看到,如果給的長度比較小他就會去刪除 Array 中的數值。如果比較大的話,他就會自己增加一些 `undefined` 的數值進去 + +### 結語 + +為了達到更好的效率,建議使用 `for` 迴圈還有暫存 `length` 的屬性。 +而 `for in` 迴圈則是會讓程式中有更多的錯誤和性能問題。 + diff --git a/external/JavaScript-Garden/doc/zhtw/core/delete.md b/external/JavaScript-Garden/doc/zhtw/core/delete.md new file mode 100644 index 0000000..091693b --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/core/delete.md @@ -0,0 +1,78 @@ +## `delete` 控制符 + +簡單來說,那是 *不可能* 去刪除一個全域變數,函式和其他東西在 JavaScript 中有一個 `DontDelete` 的屬性 + +### 全域和函式 + +當一個變數或是一個函式在一個全域範圍被定義或是在一個 [funciton scope](#function.scopes) ,這些屬性可能是動態的物件或是全域的物件。這些特性有一系列的屬性。其中一個就是 `DontDelete`。 +在這些變數和函式的宣告都會有一個屬性叫 `DontDelete`,這會使得它無法被刪除。 + + // 全域變數 + var a = 1; // DontDelete 屬性被建立 + delete a; // false + a; // 1 + + // normal function: + function f() {} // DontDelete 屬性被建立 + delete f; // false + typeof f; // "function" + + // reassigning doesn't help: + f = 1; + delete f; // false + f; // 1 + +### 明確的屬性 + +明確的屬性可以被簡單的刪除。 + + // explicitly set property: + var obj = {x: 1}; + obj.y = 2; + delete obj.x; // true + delete obj.y; // true + obj.x; // undefined + obj.y; // undefined + +在上面的例子中, `obj.x` 和 `obj.y` 可以被刪除是因為他們沒有 `DontDelete` 的屬性。 +所以下面的例子也可以這樣用。 + + // 可以運作,除了 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 + +這裡我們想要去刪除 `a`。 [`this`](#funciton.this) 這裡指向一個全域的物件,和我們明確了地定義 `a` 是它的屬性,所以可以刪除它。 + +IE 有些臭蟲,所以上面的程式碼無法使用(至少 6~8) + +### 函式的參數和內建 + +函式的普通參數,[`arguments` object](#function.arguments) 還有一些內建的屬性都有 `DontDelete` 的建立 + + // function 參數和屬性 + (function (x) { + + delete arguments; // false + typeof arguments; // "object" + + delete x; // false + x; // 1 + + function f(){} + delete f.length; // false + typeof f.length; // "number" + + })(1); + +### 接受物件 + +控制符可以接受無法預測的物件。由於一些特別的情況,會允許它能夠 `delete` + +### 結語 + +`delete` 控制符通常都有難以預料的行為,所以我們只可以安全的刪除顯著的屬性在普通的物件上。 + + diff --git a/external/JavaScript-Garden/doc/zhtw/core/eval.md b/external/JavaScript-Garden/doc/zhtw/core/eval.md new file mode 100644 index 0000000..26b7d33 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/core/eval.md @@ -0,0 +1,42 @@ +## 為什麼不要使用 `eval` + +因為 `eval` 函數會在 Javascript 的區域性的區間執行那段程式碼。 + + var foo = 1; + function test() { + var foo = 2; + eval('foo = 3'); + return foo; + } + test(); // 3 + foo; // 1 + +但是, `eval` 只接受直接的呼叫而且那個函數只能叫做 `eval`,才能在一個區段中執行。 + + var foo = 1; + function test() { + var foo = 2; + var bar = eval; + bar('foo = 3'); + return foo; + } + test(); // 2 + foo; // 3 + +所有的 `eval` 都應該去比免試用。有 99.9% 的使用情況都可以 **不必** 使用到而達到同等效果。 + +### 偽裝的 `eval` + +[定時函數](#other.timeouts) `setTimeout` 和 `setInterval` 都可以接受一個字串當做他們第一個參數。這些字串 **永遠** 都會在全域範圍內執行,因此在這種情況下 `eval` 沒有被直接的使用。 + +### 安全上的顧慮 + +`eval` 同樣有安全上的問題,因為所有的程式碼都可以被直接執行。 +而他不應去執行一串未知的字串或是來自不信任的來源。 + +### 結語 + +`eval` 應該永遠不要去只用它,任何的程式在被他執行後都有性能和安全上的考慮。如果有情況需要去使用他,他都不應該列為第一順位的解決方法。 + +應該有更好的方法能夠去使用,但是最好都不要去使用 `eval`。 + diff --git a/external/JavaScript-Garden/doc/zhtw/core/semicolon.md b/external/JavaScript-Garden/doc/zhtw/core/semicolon.md new file mode 100644 index 0000000..aab7135 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/core/semicolon.md @@ -0,0 +1,103 @@ +## 自動插入分號 + +雖然 JavaScript 有 C 語言的語法,但是他不強制一定要加上分號。 +所以分號可以被忽略。 + +Javascript 並 **不是** 一個不需要分號的語言。實際上,它需要分號來讓程式碼更容易被理解。因此 Javascript 的編譯器中遇到了缺少分號的情形,它會自動的在程式碼中插入分號。 + + var foo = function() { + } // 編輯錯誤,因沒分號 + test() + +這時候編譯器在編輯的時候,會自動的加上分號,然後重新編輯。 + + var foo = function() { + }; // 沒有錯誤,編輯繼續 + test() + +自動的加入分號是被認為 **最大** 的設計缺陷之一,因為它能改變程式碼的行為。 + +### 工作原理 + +下面的程式碼中沒有使用任何的分號,所以編譯器需要去決定在哪些地方加入分號。 + + (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) + +下面的程式碼是編譯器 **猜測** 的結果。 + + (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; // <- 插入分號,改變了 return 的表達行為 + { // 作為另一個程式碼的處理 + + // 被當做一個獨立的函數來看 + foo: function() {} + }; // <- 插入分號 + } + window.test = test; // <- 插入分號 + + // 兩行又被合併 + })(window)(function(window) { + window.someLibrary = {}; // <- 插入分號 + + })(window); //<- 插入分號 + +> **注意:** 在這個範例中 Javascript 編譯器沒有正確的處理 `return` ,因為緊接的換行符號。 +> 雖然這不能算是自動分號插入的錯誤,但是它是非常不樂見的效果。 + +編譯器在上面的程式碼中改變了原本程式碼的行為。在一些情況下,會做出 **錯誤的行為** + +### 前置括號 + +在這種前置括號的情況下,編譯器 **不會** 自動的插入分號。 + + log('testing!') + (options.list || []).forEach(function(i) {}) + +上面的程式碼被編譯器轉為只有一行程式 + + log('testing!')(options.list || []).forEach(function(i) {}) + +以上的範例中 `log` 有 **很大** 的可能 **不是** 回傳一個函數。然而這個情況下會出現 `TypeError` 的錯誤或是會出現 `undefined is not a function` . + +### 結語 + +建議永遠 **不要** 忽略分號。同樣的也建議大括號應在他對應的表達式在同一行。在 `if... else...`的表達式中也是如此,不應省略大括號。 +這個習慣可以不僅僅是讓你的程式更一致,也可以避免編譯器因為改變程式而出錯。 + diff --git a/external/JavaScript-Garden/doc/zhtw/core/undefined.md b/external/JavaScript-Garden/doc/zhtw/core/undefined.md new file mode 100644 index 0000000..501dfb9 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/core/undefined.md @@ -0,0 +1,53 @@ +## `undefined` 和 `null` + +JavaScript 中有兩個表示空值的方式, `null` 和 `undefined` , `undefined`是比較常用的一種。 + +### `undefined` 的值 + +`undefined` 是一個值為 `undefined` 的類型。 + +語言中也定義了一個全域變數,它的值為 `undefined`,這個變數的被稱作 `undefined` 。 +這個變數 **不是** 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。 + +> **ES5 提示: ** `undefined` 在 ECMAScript 5 裡 **不再是** *可寫* 的 +> 但是它的名稱還是可以被隱藏,比如說定義一個函數為 `undefined`。 + +這裡有一些例子會回傳 `undefined` 的值: + + - 進入尚未修改的全域變數 `undefined`。 + - 進入一個宣告但 **尚未** 初始化的變數。 + - `return` 表示式中沒有返回任何內容。 + - 呼叫不存在的屬性。 + - 函式參數沒有被傳遞數值。 + - 任何被被設定為 `undefined` 的變數。 + - 任何表達式中形式為 `void(expression)` + +### 處理 `undefined` 值的改變 + +由於全域變數 `undefined` 只有保存 `undefined` 類型實際值的一個副本,指定了一個新的值並 **不會** 改變 `undefined`類型裡面的值。 + +為了避免去改變 `undefined` 的值,常用的技巧就是加上一個新的變數到 [匿名包裝器](#function.scopes)。在使用的時候,這個參數不會接受任何的值。 + + var undefined = 123; + (function(something, foo, undefined) { + // undefined 在區域區間內得到了 `undefined` 的值 + + })('Hello World', 42); + +另外一個可以得到同樣的效果就是在內部宣告一個變數 + + var undefined = 123; + (function(something, foo) { + var undefined; + ... + + })('Hello World', 42); + +唯一的不同就是在下者會多 4 個多 bytes 用來壓縮檔案,而且函數內也沒有其他需要使用 `var` + +### 使用 `null` + +JavaScript 中所使用的 `undefined` 類似別的語言中的 *null* , 但實際上在 JavaScript 中的 `null` 算是另外一個類型。 + +它在 JavaScript 有些可以使用的地方 (例如說宣告一個原型的終結,例如 `Foo.prototype = null` )。 +但是在大部分的時候可以用 `undefined`,來取代。 diff --git a/external/JavaScript-Garden/doc/zhtw/function/arguments.md b/external/JavaScript-Garden/doc/zhtw/function/arguments.md new file mode 100644 index 0000000..39aa58f --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/arguments.md @@ -0,0 +1,103 @@ +## `arguments` 物件 + +所有函數在 JavaScript 中都可以有個特別的參數 `arguments`。 +這個變數掌握了一列傳入函數中的參數 + +> **注意:** 由於 `arguments` 都已經在函數中被定義了 +> 經過 `var` 定義或是用 `arguments` 宣告參數 +> `arguments` 物件都不會被建立 + +`arguments` 物件 **不是** 一個 `Array`,雖然都有很多 Array 的語法 - 就像是 `length` 屬性 - 但是它沒有繼承來自 `Array.prototype` 事實上它繼承 `object`。 + +由於這些原因,這 **不可能** 用 Array 的一些功能像是 `push`、`pop`或是 `slice` 在 `arguments`。 +但是像 `for` 迴圈這些迴圈都是可以用的,如果真的需要使用一些標準的 `Array` 功能可以先把它轉成真的 `Array` 再去使用。 + +### 轉為 Array + +下面的程式可以回傳一個新的 `Array` 包含所有的元素在 `Arguments`的物件中 + + Array.prototype.slice.call(arguments); + +這種轉化方式比較 **慢** ,如果在追求效率的程式中,不建議使用這種作法。 + + +### 傳遞參數 + +下面是建議用這種方式去傳參數到另一個函數 + + function foo() { + bar.apply(null, arguments); + } + function bar(a, b, c) { + // 在這裡做一些事情 + } + +另一個技巧是用 `call` 和 `apply` 放在一起來創造一個更快的解綁定包裝器 + + function Foo() {} + + Foo.prototype.method = function(a, b, c) { + console.log(this, a, b, c); + }; + + // Create an unbound version of "method" + // 輸入的參數: this, arg1, arg2...argN + Foo.method = function() { + + // 結果: Foo.prototype.method.call(this, arg1, arg2... argN) + Function.call.apply(Foo.prototype.method, arguments); + }; + + +### 自動更新 + +在 `Arguments` 物件創造的 *getter* 和 *setter* 的函數方法,可以被視為原本函數的變數。 + +因此,改變了一個形式參將數會跟著改變對應的 `arguments` 的屬性,反之亦然。 + + 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); + +### 性能 + +`arguments` 總是會被宣告,但除了兩個情況,一個是在一個函式中或是在其中一個參入。而不論他是否有被使用。 + +*getters* 和 *setter* 會永遠被創造。然而,他們對任何性能都沒有影響,除非對它的屬性有多次的訪問 + + +> **ES5 提示:** 那些 *getters* 和 *setters* 在嚴格的模式像不會被建立 + +然而會有一種情況來降低 JavaScript 引擎的效能。就是使用 `arguments.callee`。 + + function foo() { + arguments.callee; // 做一些在這個函數物件 + arguments.callee.caller; // 然後呼叫這個函數物件 + } + + function bigLoop() { + for(var i = 0; i < 100000; i++) { + foo(); // 通常會在內聯 + } + } + +在上面的程式中, `foo` 不再是一個單存的互聯函數 +因為它需要知道他自己和它的調用者。 +這不僅減低了它的性能,而且還破壞的封裝 + +**強烈建議不要使用** `arguments.callee` 或是其他它的屬性 + +> **ES5 Note:** 在嚴格的模式下 `arguments.callee` 會丟出一個 `TypeError`, 因為這種方法已經被廢除了 + +[1]: http://en.wikipedia.org/wiki/Inlining + + diff --git a/external/JavaScript-Garden/doc/zhtw/function/closures.md b/external/JavaScript-Garden/doc/zhtw/function/closures.md new file mode 100644 index 0000000..11bcda4 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/closures.md @@ -0,0 +1,85 @@ +## Closures 和 References + +JavaScript 有一個很重要的特徵就是 **closures** +因為有 Closures,所以作用域 **永遠** 能夠去訪問作用區間外面的變數。 +[函數區間](#function.scopes) 是JavaScript 中唯一擁有自生作用域的結構,因此 Closures 的創立需要依賴函數 + +### 模仿私有變數 + + function Counter(start) { + var count = start; + return { + increment: function() { + count++; + }, + + get: function() { + return count; + } + } + } + + var foo = Counter(4); + foo.increment(); + foo.get(); // 5 + +這裡,`Counter` 返回兩個 Closures,函數 `increment` 還有 `get`。這兩個函數都維持著對外部作用域 `Counter` 的引用,因此總可以訪問作用域的變數 `count`。 + + +### 為什麼不可以在外部訪問私有變數 + +因為 Javascript **不可以** 對作用域進行引用或賦值。因此外部的地方沒有辦法訪問 `count` 變數。 +唯一的途徑就是經過那兩個 Closures + + var foo = new Counter(4); + foo.hack = function() { + count = 1337; + }; + +在上面的例子中 `count` **不會** 改變到 `Counter` 裡面的 `count` 的值。因為 `foo.hack` 沒有在 **那個** 作用域內被宣告。它只有會覆蓋或者建立在一個 **全域** 的變數 `count` + +### 在循環內的 Closures + +一個常見的錯誤就是在 Closures 中使用迴圈,假設我們要使用每次迴圈中所使用的進入變數 + + for(var i = 0; i < 10; i++) { + setTimeout(function() { + console.log(i); + }, 1000); + } + +在上面的例子中它 **不會** 輸出數字從 `0` 到 `9`,但只會出現數字 `10` 十次。 +在 `console.log` 被呼叫的時候,這個 *匿名* 函數中保持一個 **參考** 到 i ,此時 `for`迴圈已經結束, `i` 的值被修改成了 `10`。 +為了要達到想要的結果,需要在每次創造 **副本** 來儲存 `i` 的變數。 + +### 避免引用錯誤 + +為了要有達到正確的效果,最好是把它包在一個 +[匿名函數](#function.scopes). + + for(var i = 0; i < 10; i++) { + (function(e) { + setTimeout(function() { + console.log(e); + }, 1000); + })(i); + } + +匿名外部的函數被呼叫,並把 `i` 作為它第一個參數,此時函數內 `e` 變數就擁有了一個 `i` 的拷貝。 +當傳遞給 `setTimeout` 這個匿名函數執行時,它就擁有了對 `e` 的引用,而這個值 **不會** 被循環改變。 +另外有一個方法也可以完成這樣的工作,那就是在匿名函數中返回一個函數,這和上面的程式碼有同樣的效果。 + + for(var i = 0; i < 10; i++) { + setTimeout((function(e) { + return function() { + console.log(e); + } + })(i), 1000) + } + +另外也可以透過 `.bind` 完成此工作,它可以將 `this` 及參數傳入函數內,行為就如同上面程式碼一樣。 + + for(var i = 0; i < 10; i++) { + setTimeout(console.log.bind(console, i), 1000); + } + diff --git a/external/JavaScript-Garden/doc/zhtw/function/constructors.md b/external/JavaScript-Garden/doc/zhtw/function/constructors.md new file mode 100644 index 0000000..a74a10d --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/constructors.md @@ -0,0 +1,101 @@ +## 建構函式 + +JavaScript 中的建構函式和其他語言中的建構函式是不同的。 +用 `new` 的關鍵字方式調用的函式都被認為是建構函式。 +在建構函式內部 - 被呼叫的函式 - `this` 指向一個新建立的 `object`。[prototype](#object.prototype) 這是一個新的物件一個被指向函式的 `prototype` 的建構函式。 + +如果被使用的函式沒有明顯的呼叫 `return` 的表達式,它會回傳一個隱性的 `this` 的新物件。 + + function Foo() { + this.bla = 1; + } + + Foo.prototype.test = function() { + console.log(this.bla); + }; + + var test = new Foo(); + +在上面的例子中 `Foo` 建立一個建構函式,並設立一個 `prototype` 來創建一個新的物件叫 `Foo.prototype`。 +這個情況下它顯示的 `return` 一個表達式,但他 **只** 返回一個 `Object`。 + + function Bar() { + return 2; + } + new Bar(); // 返回一個新物件 + + function Test() { + this.value = 2; + + return { + foo: 1 + }; + } + new Test(); // 回傳物件 + +如果 `new` 的關鍵字被忽略,函式就 **不會** 回傳一個新的物件。 + + function Foo() { + this.bla = 1; // 獲取一個全域的參數 + } + Foo(); // undefined + +雖然上面有些情況也能正常運行,但是由於 JavaScript 中 [`this`](#funciton.this) 的工作原理,這裡的 `this` 指向 *全域對象*。 + +### 工廠模式 + +為了不使用 `new` 關鍵字,建構函式必須顯性的返回一個值。 + + function Bar() { + var value = 1; + return { + method: function() { + return value; + } + } + } + Bar.prototype = { + foo: function() {} + }; + + new Bar(); + Bar(); + +上面兩個呼叫 `Bar` 的方法回傳的值都一樣,一個新創建的擁有 `method` 屬性被返回,這裡創建了一個 [Closure](#function.closures). + +還有注意, `new Bar()` 並 **不會** 改變返回物件的原型。 +因為建構函式的原型會指向剛剛創立的新物件,而在這裡的 `Bar` 沒有把這個新物件返回。 +在上面的例子中,使用或者不使用 `new` 關鍵字沒有什麼功能性的區別 + + +### 通過工廠模式創建的新對象 + +常聽到建議 **不要** 使用 `new`,因為如果忘記如何使用它會造成錯誤。 +為了創建一個新的物件,我們可以用工廠方法,來創造一個新的物件在那個方法中。 + + function Foo() { + var obj = {}; + obj.value = 'blub'; + + var private = 2; + obj.someMethod = function(value) { + this.value = value; + } + + obj.getPrivate = function() { + return private; + } + return obj; + } + +雖然上面的方式比起 `new` 的調用方式更不容易出錯,並且可以充分的使用 [私有變數](#function.closures)所帶來的便利,但是還是有一些不好的地方 + + +1. 會占用更多的記憶體,因為創建的物件 **沒有** 辦法放在在同一個原型上。 +2. 為了要用繼承的方式,工廠方法需要複製所有的屬性或是把一個物件作為新的物件的原型。 +3. 放棄原型鏈僅僅是因為防止遺漏 `new` 所帶來的問題,這與語言本身的思想鄉違背。 + +### 結語 + +雖然遺漏 `new` 關鍵字可能會導致問題,但這並 **不是** 放棄只用原型的藉口。 +最終使用哪種方式取決於應用程式的需求,選擇一種程式語言風格並堅持下去才是最重要的。 diff --git a/external/JavaScript-Garden/doc/zhtw/function/general.md b/external/JavaScript-Garden/doc/zhtw/function/general.md new file mode 100644 index 0000000..fe40088 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/general.md @@ -0,0 +1,43 @@ +## 函式的宣告和表達方式 + +函式在 JavaScript 是第一等物件。這表示他們可以把函式當做值一樣傳遞。 +一個常見的用法是用 *匿名函式* 當做一個回傳去呼叫另一個函式,這是一種非同步函式 + +### 函式的宣告 + + function foo() {} + +上面的函式在被執行之前會被 [解析(hoisted)](#function.scopes),因此它可以在 **任意** 的地方都是 *有宣告的* ,就算是在比這個函式還早呼叫。 + + + foo(); // 可以執行,因為 foo 已經在運行前就被建立 + function foo() {} + +### `function` 的表達式 + + var foo = function() {}; + +這個例子把一個 *匿名* 函式賦值給變數 `foo`。 + + foo; // 'undefined' + foo(); // 錯誤: TypeError + var foo = function() {}; + +由於 `var` 已經宣告變數 `foo` 在所有的程式碼執行之前。 +所以 `foo`已經在程式運行前就已經被定義過了。 +但是因為賦值只會在運行時去執行,所以在程式碼執行前,`foo` 的值還沒被宣告所以為 [undefined](#core.undefined)。 + + +### 命名函式的賦值表達式 + +另一個特殊狀況是將一個命名函式賦值給一個變數。 + + var foo = function bar() { + bar(); // 可以運行 + } + bar(); // 錯誤:ReferenceError + +`bar` 不可以在外部的區域被執行,因為它只有在 `foo` 的函式內才可以執行。 +然而在 `bar` 內部還是可以看見。這是由於 JavaScript的 [命名處理](#function.scopes)所致。 +函式名在函式內 *都* 可以使用。 + diff --git a/external/JavaScript-Garden/doc/zhtw/function/scopes.md b/external/JavaScript-Garden/doc/zhtw/function/scopes.md new file mode 100644 index 0000000..d443656 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/scopes.md @@ -0,0 +1,199 @@ +## 作用域和命名空間 + +儘管 JavaScript 支持一個大括號創建的程式碼,但並不支持塊級作用域。 +而僅僅支援 *函式作用域* + + function test() { // 一個作用域 + for(var i = 0; i < 10; i++) { // 不是一個作用域 + // 算數 + } + console.log(i); // 10 + } + +> **注意:** 如果不是用在變數賦予值的宣告中、return 的回傳值中或者函數的參數中, `{...}` 將會被解析為一個程式區塊(block scope),而不是被解析為物件實體語法(object literal)。 +> 如果考慮到 [自動分號插入](#core.semicolon),可能會造成一些不易察覺的錯誤。 + +JavaScript 中沒有寫示的命名空間定義,這代表著它所有定義的東西都是 *全域共享* 在同一個命名空間下。 + +每次引用一個變數,JavaScript 會向上找整個作用域直到找到這個變數為止。 +如果在全域中無法找到那個變數,它會拋出 `ReferenceError` 錯誤碼。 + +### 全域變數的壞處 + + // script A + foo = '42'; + + // script B + var foo = '42' + +上面兩個腳本 *不會* 有同樣的效果。腳本 A 在 *全域* 空間定義了變數 `foo`,腳本 B 定義了 `foo` 在目前的區間內。 + +再次強調,上面的效果是 **完全不同**,不使用 `var` 會導致隱性的全域變數。 + + // 全域作用區 + var foo = 42; + function test() { + // 局部作用區 + foo = 21; + } + test(); + foo; // 21 + +在函數 `test` 中部使用 `var` 會覆蓋到原本在外面的 `foo`。 +雖然看起來不是什麼大問題,但是當程式有幾千行的時候沒有使用 `var` 會照成難以追蹤的臭蟲。 + + + // 全域作用域 + var items = [/* some list */]; + for(var i = 0; i < 10; i++) { + subLoop(); + } + + function subLoop() { + // subLoop 的作用域 + for(i = 0; i < 10; i++) { // 缺少了 var + // 做一些事情 + } + } + +在外面的迴圈在呼叫第一次 `subLoop` 之後就會停止,因為 `subLoop` 全域變數中的 `i` 被覆蓋了。 +在第二次使用 `for` 迴圈的時候,使用 `var` 就可以避免這種錯誤。 +在宣告變數的時候 **絕對不要** 忘記 `var`,除非就是 `希望他的效果` 是取改變外部的作用域。 + +### 局部變數 + +在 javascript 中能用兩種方式來宣告局部變數。 +[函式](#function.general) 參數和透過 `var` 來宣告變數。 + + // 全域變數 + var foo = 1; + var bar = 2; + var i = 2; + + function test(i) { + // 函式 test 內部的局部作用域 + i = 5; + + var foo = 3; + bar = 4; + } + test(10); + +`foo` 和 `i` 是它的局部變數在 `test` 函式中,但是在 `bar` 的賦值會覆蓋全區域的作用域內的同名變數。 + +### 變數宣告 + +JavaScript 會 **提昇** 變數宣告, 這代表著 `var` 和 `function` 的圈告都會被提升到當前作用域的頂端。 + + 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]; + } + } + +在上面的程式碼會被轉化在執行之前。 JavaScript 會把 `var`,和 `function` 宣告,放到最頂端最接近的作用區間 + + // var 被移到這裡 + var bar, someValue; // 值等於 'undefined' + + // function 的宣告也被搬上來 + function test(data) { + var goo, i, e; // 沒有作用域的也被搬至頂端 + if (false) { + goo = 1; + + } else { + goo = 2; + } + for(i = 0; i < 100; i++) { + e = data[i]; + } + } + + bar(); // 出錯:TypeError , bar 還是 'undefined' + someValue = 42; // 賦值語句不會被提昇規則影響 + bar = function() {}; + + test(); + +沒有作用域區間不只會把 `var` 放到迴圈之外,還會使得 `if` 表達式更難看懂。 + +在一般的程式中,雖然 `if` 表達式中看起來修改了 *全域變數* `goo`,但實際上在提昇規則被運用後,卻是在修改 *局部變數* + +如果沒有提昇規則的話,可能會出現像下面的看起來會出現 `ReferenceError` 的錯誤。 + + // 檢查 SomeImportantThing 是否已經被初始化 + if (!SomeImportantThing) { + var SomeImportantThing = {}; + } + +但是它沒有錯誤,因為 `var` 的表達式會被提升到 *全域作用域* 的頂端。 + + var SomeImportantThing; + + // 有些程式,可能會初始化。 + SomeImportantThing here, or not + + // 檢查是否已經被初始化。 + if (!SomeImportantThing) { + SomeImportantThing = {}; + } + +### 名稱解析順序 + +JavaScript 中所有的作用區,包括 *全域作用域*,都有一個特殊的名字 [`this`](#function.this), 在它們裡面被定義,指向當前的物件 + +函式作用域也有一個名稱叫做 [`arguments`](#function.arguments), 定義它們,其中包括傳到函式內的參數。 + +例如,它們開始試著進入到 `foo` 的作用域裡面, JavaScript 會依照下面的順序去查詢: + + 1. 當作用域內是否有 `var foo` 的定義。 + 2. 函式形式參數是否有使用 `foo` 名稱定義。 + 3. 函式自身是否叫做 `foo`。 + 4. 回溯到上一個層級然後再從第一個開始往下去查。 + +> **注意: ** 自定義 `arguments` 參數會阻止原生的 `arguments` 的物件創立 + +### 命名空間 + +只有一個全域作用域會導致常見的錯誤是命名衝突。在 JavaScript 中可以透過 *匿名包裝器* 來解決。 + + (function() { + // 自己本身的匿名空間 + + window.foo = function() { + // 對外公開的函式 + }; + + })(); // 馬上執行這個匿名函式 + +匿名函式被認為是 [表達式](#function.general)因此為了要可以調用,它們會先被執行。 + + ( // 小括號內的先被執行 + function() {} + ) // 回傳函數對象 + () // 調用上面的執行結果 + +還有其他方式也可以像上面一樣調用函式的方式達到 + + !function(){}() + +function(){}() + (function(){}()); + // and so on... + +### 結語 + +建議最好是都用 *匿名包裝器* 來封裝你的程式碼在自己的命名區間內。這不僅是要防止命名衝突也可以使得程序更有模組化。 + +另外,全域變數是個 **不好的** 習慣,因為它會帶來錯誤和更難去維護。 diff --git a/external/JavaScript-Garden/doc/zhtw/function/this.md b/external/JavaScript-Garden/doc/zhtw/function/this.md new file mode 100644 index 0000000..361a06c --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/function/this.md @@ -0,0 +1,105 @@ +## `this` 的工作原理 + +JavaScript 有一道完全不屬於其他語言處理 `this` 的處理機制。 +在 **五** 種不同的情況下, `this` 指向的各不相同 + +### 全域變數 + + this; + +如果再全域範圍內使用 `this`,會指向 *全域* 的物件 + + +### 呼叫一個函式 + + foo(); + +這裡 `this` 也會指向 *全域* 物件。 + +> **ES5 注意:** 在嚴格模式下,不存在全域變數。 +> `this` 將會是 `undefined`。 + +### 呼叫一個方法 + + test.foo(); + +這個例子中, `this` 指向 `test` 物件。 + +### 呼叫一個建構函式 + + new foo(); + +如果函式傾向用 `new` 關鍵詞使用,我們稱這個函式為 [建構函式](#function.constructors)。 +在函式內部, `this` 指向 *新物件的創立* + +### 顯示的設置 `this` + + function foo(a, b, c) {} + + var bar = {}; + foo.apply(bar, [1, 2, 3]); // Array 會被擴展,如下所示 + foo.call(bar, 1, 2, 3); // 傳遞參數 a = 1, b = 2, c = 3 + +當使用 `function.prototype` 上的 `call` 或只 `apply` 方法時,函式內的 `this` 將會被 **顯示設置** 為函式調用的第一個參數。 + +因此,在以上的例子中已不適用*函式調用*的原則,而且`this`會被設定指向`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. + +### 常見誤解 + +儘管大部分的例子都合理,但第一個例子(譯者注: 應該是指前面呼叫一個函式的那個例子)可以被視為一個語言的不良設計,因為它**從來**就沒有實際用途。 + + Foo.method = function() { + function test() { + // this 設定為全域 + } + test(); + }; + +一個常見的誤解是 `test` 中的 `this` 指向 `Foo` 物件,但實際上並**不是**。 + +為了在 `test` 中使用 `Foo` 物件,我們需要在 `method` 函式内部建立一個區域變數指向 `Foo`。 + + Foo.method = function() { + var that = this; + function test() { + // 這裡使用 that 而非 this + } + test(); + }; + +`that` 只是普通的名字,不過這個名字常被用用來指向外部的 `this`。 在 [閉包](#function.closures) 一節,可以看到它(`that`)可以取代 `this` 傳遞。 + +在 ECMAScript 5 ,你可以使用 `bind` 結合匿名函式達到相同結果。 + + Foo.method = function() { + var test = function() { + // this 指向 Foo + }.bind(this); + test(); + }; + +### 函式表達式 + +另一個在 JavaScript 中**不會**運作的就是 function aliasing,也就是函式**賦值**給一個變數。 + + var test = someObject.methodTest; + test(); + +上例中,`test` 就像一個普通的函式被调用;因此,函式内的 this 將不再指向 `someObject`。 + +雖然起初 `this` 的绑定特性似乎像是個壞主意,但事實上,它使得 [原型繼承](#object.prototype)得以運作。 + + function Foo() {} + Foo.prototype.method = function() {}; + + function Bar() {} + Bar.prototype = Foo.prototype; + + new Bar().method(); + +當 `method` 被呼叫時,`this` 將會指向 `Bar` 的實體物件。 + diff --git a/external/JavaScript-Garden/doc/zhtw/index.json b/external/JavaScript-Garden/doc/zhtw/index.json new file mode 100644 index 0000000..37533c1 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/index.json @@ -0,0 +1,69 @@ +{ + "title": "JavaScript Garden", + "langTitle": "JavaScript Garden 繁體中文翻譯", + "description": "JavaScript 語言中古怪用法及缺點的文件總集", + "sections": [ + { + "title": "簡介", + "dir": "intro", + "articles": ["index"] + }, + { + "title": "物件", + "dir": "object", + "articles": [ + "general", + "prototype", + "hasownproperty", + "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" + ] + }, + { + "title": "核心", + "dir": "core", + "articles": [ + "eval", + "undefined", + "semicolon", + "delete" + ] + }, + { + "title": "其他", + "dir": "other", + "articles": [ + "timeouts" + ] + } + ] +} diff --git a/external/JavaScript-Garden/doc/zhtw/intro/index.md b/external/JavaScript-Garden/doc/zhtw/intro/index.md new file mode 100644 index 0000000..5de4222 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/intro/index.md @@ -0,0 +1,53 @@ +## 簡介 + +JavaScript Garden 是一個不斷更新的文件,最主要是要去了解一些 Javascript 比較古怪的部份。 +給一些意見來防止遇到一些常見的錯誤和一些難以發現的問題,以及性能問題和不好的習慣。 +初學者也可以藉此去了解 Javascript 這項語言的特性。 + +JavaScript Garden 並 **不是** 要教導你 Javascript 的語言。 +如果要能夠理解這篇文章的內容,你需要事先學習 JavaScript 的基礎知識。 +在 Mozilla 開發者網路中有一系列非常棒的學習[guide][1]。 + + +## 作者 + +這個使用手冊是來自於 [Stack Overflow][2] 的使用者, [Ivo Wetzel][3] +(寫作) 和 [Zhang Yi Jiang][4] (設計)。 + +目前為 [Tim Ruffles](http://truffles.me.uk) 維護此專案。 + +## 貢獻者 + +- [貢獻者](https://github.com/BonsaiDen/JavaScript-Garden/graphs/contributors) + +## 繁體中文翻譯 + + - [紀力榮][29] + - [張仲威][30] + - [Bo-Yi Wu][31] + +## 存放空間 + +JavaScript Garden 目前存放於 GitHub,但是 [Cramer Development][7] 讓我們有另一個存放位置在 [JavaScriptGarden.info][8]。 + +## 許可 + +JavaScript Garden 是在 [MIT license][9] 許可協議下發佈,並存在於 +[GitHub][10],如果你有發現錯誤或是打字上的錯誤 [新增一個任務][11] 或者發一個請求。 +你也可以在 StackOverflow 的 [JavaScript room][12] 上面找到我們。 + +[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 +[29]: https://github.com/chilijung +[30]: https://github.com/wwwy3y3 +[31]: https://github.com/appleboy diff --git a/external/JavaScript-Garden/doc/zhtw/object/forinloop.md b/external/JavaScript-Garden/doc/zhtw/object/forinloop.md new file mode 100644 index 0000000..06b248a --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/object/forinloop.md @@ -0,0 +1,40 @@ +## `for in` 迴圈 + +就像其他的 `in` 操作符一樣, `for in` 循環也進入所有在物件中的屬性 + +> **注意: ** `for in` 迴圈 **不會** 進入那些 `enumerable` 屬性是 `false`,舉例來說,陣列中 `length` 的屬性 + + // 修改 Object.prototype + Object.prototype.bar = 1; + + var foo = {moo: 2}; + for(var i in foo) { + console.log(i); // 輸出兩個屬性:bar 和 moo + } + +由於不可能改變 `for in` 本身的行為,因為有必要過濾出那些不希望在迴圈出現的屬性,這可以用 `Object.prototype` 原型上的 [`hasOwnProperty`](#object.hasownproperty) 的函數來完成。 + +> **注意: ** 由於 `for in` 總是要到所有原型鏈裡,因此如果物件的繼承層次太深的話會影響性能。 + + +### 用 `hasOwnProperty` 來過濾 + + // foo 變數是上面範例中的 + for(var i in foo) { + if (foo.hasOwnProperty(i)) { + console.log(i); + } + } + +這個版本的程式碼是唯一正確的寫法。由於我們使用了 `hasOwnProperty`,這次 **只** 輸出 `moo`。 +如果不使用這個程式碼在原型物件中(比如 `Object.prototype`)被擴展可能會出錯。 + +一個廣泛使用的舊函式庫 [Prototype][1]就擴展了原型的 JavaScript 物件。 +因此,當這個函式庫包含在頁面中時,不使用 `hasOwnProperty` 過濾的 `for in` 迴圈難免會出問題。 + +### 總結 + +推薦 **總是** 使用 `hasOwnProperty`。不要對程式碼的環境做任何假設,不要假設原生的對象是否被擴張。 + +[1]: http://www.prototypejs.org/ + diff --git a/external/JavaScript-Garden/doc/zhtw/object/general.md b/external/JavaScript-Garden/doc/zhtw/object/general.md new file mode 100644 index 0000000..6cd5660 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/object/general.md @@ -0,0 +1,85 @@ +## 物件的使用和屬性 + +在 Javascript 中全部都是物件,除了 [`null`](#core.undefined) 和 [`undefined`](#core.undefined)。 + + false.toString(); // 'false' + [1, 2, 3].toString(); // '1,2,3' + + function Foo(){} + Foo.bar = 1; + Foo.bar; // 1 + +一個常見的誤解就是字面值(literal)不是物件。這是因為 JavaScript 編譯器的一個錯誤,它試圖把 *點操作符* 解析為浮點數的字面值的一部分。 + + 2.toString(); // 出錯: SyntaxError + +有很多變通方法可以讓數字的字面值看起來像物件。 + + 2..toString(); // 第二個點號可以正常解析 + 2 .toString(); // 注意點號前面的空格 + (2).toString(); // 2 先被計算 + +### 物件做為數據類型 + +JavaScript 的物件可以作為 [*Hashmaps*][1]使用,主要用來保存命名的鍵與值的對應關係。 + +使用物件的字面語法 - `{}` - 可以創建一個簡單的物件。 這個新創建的物件[繼承](#object.prototype) 自 `Object.prototype` ,沒有任何 [自定義屬性](#object.hasownproperty)。 + + var foo = {}; // 一個空的物件 + + // 一個新的物件,有值為 12 的自定義屬性 'test' + var bar = {test: 12}; + +### 訪問屬性 + +有兩種方式來訪問物件的屬性,點操作或是中括號操作。 + + var foo = {name: 'kitten'} + foo.name; // kitten + foo['name']; // kitten + + var get = 'name'; + foo[get]; // kitten + + foo.1234; // SyntaxError + foo['1234']; // works + +兩種語法是相等的,唯一的差別是,使用中括號允許你動態的設定屬性,使用點操作不允許屬性為變數,否則會造成語法錯誤 + +### 刪除屬性 + +唯一刪除屬性的方式就是用 `delete` 操作符。設置屬性為 `undefined` 或是 `null` 只有刪除的屬性和值的關聯,沒有真的刪掉屬性 + + 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]); + } + } + +上面的輸出結果有 `bar undefined` 和 `foo null` +只有 `baz` 真正被刪除而已,所以從輸出結果中消失。 + + +### 屬性名的語法 + + var test = { + 'case': 'I am a keyword, so I must be notated as a string', + delete: 'I am a keyword, so me too' // raises SyntaxError + }; + +物件的屬性名可以使用字符串或是普通的宣告。但是由於 JavaScript 編譯器存在一個錯誤設計。 +上面的兩種方式在 ECMAScript 5之前都會拋出 `SyntaxError` 的錯誤。 + +這個錯誤的原因是 `delete` 是 JavaScript 語言的一個 *關鍵字* 因此為了在更低的版本能執行最好用 *string literal* + +[1]: http://en.wikipedia.org/wiki/Hashmap + diff --git a/external/JavaScript-Garden/doc/zhtw/object/hasownproperty.md b/external/JavaScript-Garden/doc/zhtw/object/hasownproperty.md new file mode 100644 index 0000000..5d8403b --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/object/hasownproperty.md @@ -0,0 +1,45 @@ +## `hasOwnProperty` + +為了判斷一個物件是否包含 *自定義* 屬性而 *不是* [原型](#object.prototype)上的屬性,我們需要使用繼承 `Object.prototype` 的 `hasOwnProperty` 方法。 + +> **注意:** 判斷一個屬性是否 `undefined` 是 **不夠的**。 +> 因為一個屬性可能存在,但是它的值被設成 `undefined`。 + +`hasOwnProperty` 是 JavaScript 中唯一一個處理屬性但是 **不** 找原型鏈的函式。 + + // 修改 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 + +只有 `hasOwnProperty` 給予正確的結果,這對進入物件的屬性很有效果,**沒有** 其他方法可以用來排除原型上的屬性,而不是定義在物件 *自己* 上的屬性。 + +### `hasOwnProperty` 作為屬性 + +JavaScript **不會** 保護 `hasOwnProperty`被占用,因此如果碰到存在這個屬性,就需要使用 *外部* 的 `hasOwnProperty` 來獲取正確的結果。 + + var foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Here be dragons' + }; + + foo.hasOwnProperty('bar'); // 永遠返回 false + + // 使用其他對象的 hasOwnProperty,並將其上下設置為 foo + ({}).hasOwnProperty.call(foo, 'bar'); // true + + // 也可以透過原生 Object prototype 的 hasOwnProperty 函數來達成目的 + Object.prototype.hasOwnProperty.call(foo, 'bar'); // true + +### 結論 + +當檢查一個物件是否存在的時候, `hasOwnProperty` 是 **唯一** 可用的方法。 +同時在使用 [`for in loop`](#object.forinloop) +建議使用 `hasOwnProperty` 避免 [原型](#object.prototype)所帶來的干擾。 diff --git a/external/JavaScript-Garden/doc/zhtw/object/prototype.md b/external/JavaScript-Garden/doc/zhtw/object/prototype.md new file mode 100644 index 0000000..83ad784 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/object/prototype.md @@ -0,0 +1,87 @@ +## Prototype + +JavaScript 不包含傳統繼承的模型,它使用的是*原型*模型。 + +儘管常常有人提及 JavaScript 的缺點,但基於原型的繼承模型比傳統繼承更強大。 +實現傳統的類繼承模型是很簡單。但是在 JavaScript 中實現原型的繼承模型則要困難很多。 + +由於 JavaScript 是唯一一個被廣泛使用的基於原型繼承的語言,所以我們必須要花時間來理解這兩者的不同。 + +第一個不同之處在於 JavaScript 使用 *原型鏈* 的繼承方式。 + +> **注意: ** 簡單的使用 `Bar.prototype = Foo.prototype` 將會導致兩個對象共享 **相同** 的原型。 +>因此,改變任一個原型都會去影響到另外一個,這在大部分的時候不是想得到的結果。 + + function Foo() { + this.value = 42; + } + Foo.prototype = { + method: function() {} + }; + + function Bar() {} + + // 設置 Bar 的 prototype 屬性為 Foo 的實例對象 + Bar.prototype = new Foo(); + Bar.prototype.foo = 'Hello World'; + + // 修正 Bar.prototype.constructor 為 Bar 本身 + Bar.prototype.constructor = Bar; + + var test = new Bar(); // 開啟一個新的實例 + + // 原型鏈 + test [instance of Bar] + Bar.prototype [instance of Foo] + { foo: 'Hello World', value: 42 } + Foo.prototype + { method: ... } + Object.prototype + { toString: ... /* etc. */ } + +上面的例子中,物件 `test` 會繼承來自 `Bar.prototype` 和 `Foo.prototype`。因此它可以進入來自 `Foo` 原型的方法 `method`。 +同時它也可以訪問 **那個** 定義在原型上的 `Foo` 實例屬性 `value`。 + +要注意的是 `new Bar()` **沒有** 創立一個新的 `Foo` 實例,它重複利用的原本的 prototype。因此, `Bar` 的實例會分享到 **相同** 的 `value` 屬性。 + +> **注意:** **不要** 使用 `Bar.prototype = Foo`,因為這不會執行 `Foo` 的原型,而是指向函式 `Foo`。 +> 因此原型鏈將回碩到 `Function.prototype` 而不是 `Foo.prototype` ,因此 `method` 將不會在 Bar 的原型鏈上。 + +### 屬性查詢 + +當查詢一個物件的屬性時,JavaScript 會 **向上** 查詢,直到查到指定名稱的屬性為止。 + +如果他查到原型鏈的頂部 - 也就是 `Object.prototype` - 但是仍然沒有指定的屬定,就會返回 [undefined](#core.undefined)。 + +### 原型屬性 + +當原型屬性用來建造原型鏈,它還是有可能去把 **任意** 類型的值給它 + + function Foo() {} + Foo.prototype = 1; // 無效 + +分派物件,在上面的例子中,將會動態的創建原型鏈。 + +### 效能 + +如果看在屬性在原型鏈的上端,對於查詢都會有不利的影響。特別的,試圖獲取一個不存在的屬性將會找遍所有原型鏈。 + +並且,當使用 [迴圈](#object.forinloop)找尋所有物件的屬性時,原型鏈上的 **所有** 屬性都會被訪問。 + +### 擴展 Native Prototype + +一個經常發生的錯誤,那就是擴展 `Object.prototype` 或者是其他內建類型的原型物件。 + +這種技術叫做 [monkey patching][1] 並且會破壞 *封裝*。雖然被廣泛的應用到一些 Javascript 的架構,像是 [Prototype](http://prototypejs.org) , 但仍然沒有好的理由新增一個 *非標準* 的功能去搞亂內建型別。 + +擴展內置類型的 **唯一** 理由是為了和新的 JavaScript 保持一致,比如說 [`Array.forEach`][3] + +### 總結 + +在寫複雜的程式碼的時候,要 **充分理解** 所有程式繼承的屬性還有原型鏈。 +還要提防原型鏈過長帶來的性能問題,並知道如何通過縮短原型鏈來提高性能。 +絕對 **不要使用** `native prototype` 除非是為了和新的 JavaScript 引擎作兼容。 + +[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/external/JavaScript-Garden/doc/zhtw/other/timeouts.md b/external/JavaScript-Garden/doc/zhtw/other/timeouts.md new file mode 100644 index 0000000..d717a81 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/other/timeouts.md @@ -0,0 +1,135 @@ +### `setTimeout` 和 `setInterval` + +由於 Javascript 具有非同步的特性,因此可以用 `setTimeout` 和 `setInterval` 來執行一個函式。 + +> **注意:** Timeouts 不在 ECMAScript 的標準中。它們是 [DOM][1] 其中的一部分 + + function foo() {} + var id = setTimeout(foo, 1000); // returns a Number > 0 + +當 `setTimeout` 被呼叫,它會回傳一個 ID 標準並且 **大約** 1000 毫秒後在在去呼叫 `foo` 函式。 +`foo` 函式只會被執行 **一次**。 + +基於 JavaScript 引擎的計時策略,以及基本的單線程運行的方式,所以其他的程式碼可以被阻塞。 +因此 **沒法確保**函式會在 `setTimeout` 指定的時可被調用。 + +第一個參數被函式呼叫的會在 *全域物件* 被呼叫,這代表 [`this`](#function.this)在這個函式會指向全域物件。 + + function Foo() { + this.value = 42; + this.method = function() { + // 指向全域 + console.log(this.value); // 會跑出 undefined + }; + setTimeout(this.method, 500); + } + new Foo(); + +> **注意: ** `setTimeout` 第一個參數是 **參數的物件**,這是一個很常見的錯誤使用 `setTimeout(foo(), 1000), +> 這裡會調用 `foo` 的 **return value** 而 **不是** `foo` 本身。 +> 如果函式返回 `undefined` , `setTimeout` 也不會出錯。 + +### `setInterval` 的堆調用 + +`setTimeout` 只會在函式上跑一次而已, `setInterval` - 則會在每隔 `X` 毫秒執行函式一次。但不鼓勵這種寫法。 + +當回傳函式的執行被阻塞時, `setInterval` 仍然會發佈更多的回傳函式。在很小的定時間隔情況像會使得回傳函式被堆疊起來。 + + function foo(){ + // 執行 1 秒 + } + setInterval(foo, 100); + +上面的程式中, `foo` 會執行一次然後被阻塞了一分鐘 + +在 `foo` 被阻塞的時候 `setInterval` 還是會組織將對回傳函式的調用。因此當第一次 `foo` 函式調用結束時,已經有 **10** 次函式的調用在等待執行。 + +### 處理可能被阻塞的調用 + +最簡單的解決方法,也是最容易控制的解決方法,就是在函式中使用 `setTimeout`。 + + function foo(){ + // something that blocks for 1 second + setTimeout(foo, 100); + } + foo(); + +這樣不只封裝了 `setTimeout`,也防止了堆疊的呼叫,還有給它更多的控制。 `foo` 可以去決定要不要繼續執行。 + +### 手動清理 Timeouts + +清除 timeouts 所產生的 ID 標準傳遞給 `clearTimeout` 或 `clearInterval` 函式來清除定時, +至於使用哪個函式取決於調用的時候使用的是 `setTimeout` 還是 `setInterval`。 + + var id = setTimeout(foo, 1000); + clearTimeout(id); + +### 清除所有 Timeouts + +由於沒有一個內建的方法可以一次清空所有的 timeouts 和 intervals,所以只有用暴力法來達到這樣的需求。 + + // clear "all" timeouts + for(var i = 1; i < 1000; i++) { + clearTimeout(i); + } + +可能還有一些定時器不會在上面的代碼中被清除,因此我們可以事先保存所有的定時器 ID,然後一把清除。 + + + // clear "all" timeouts + var biggestTimeoutId = window.setTimeout(function(){}, 1), + i; + for(i = 1; i <= biggestTimeoutId; i++) { + clearTimeout(i); + } + +### 隱藏使用 `eval` + +`setTimeout` and `setInterval` 也可以使用字串當作他們的第一個參數. +不過這個特性 **絕對** 不要使用, 因為在內部他將利用 `eval` 來實作。 + +> **注意:** 由於 timeout 函式 **並未** 被列入 ECMAScript +> 標準中,當你將字串當成參數傳入時,在不同的 Javscript +> 實作中很有可能會造成不一樣的行為。比如說:在 Microsoft 的 JScript 中,就使用 `Function` +> 建構子來取代 `eval`。 + + + + function foo() { + // will get called + } + + function bar() { + function foo() { + // never gets called + } + setTimeout('foo()', 1000); + } + bar(); + +在這個範例中,由於 `eval` 沒有被[直接](#core.eval)呼叫,在 `setTimeout` 中被傳入的字串將會在 *全域* 範圍中被執行,因此,他將不會使用在 `bar` 區域的 `foo`。 + +我們進一步建議 **不要** 用字串當作參數傳到會被 timeout 呼叫的函式中。 + + + function foo(a, b, c) {} + + // NEVER use this + setTimeout('foo(1, 2, 3)', 1000) + + // Instead use an anonymous function + setTimeout(function() { + foo(1, 2, 3); + }, 1000) + +> **注意** 儘管使用 `setTimeout(foo, 1000, 1, 2, 3)` +> 這樣的文法是可能的,但我們卻不建議這樣做,因為這和 [方法](#function.this) +> 一起使用時可能會導致微妙的錯誤。 + +### 結論 + +**絕對** 不要使用字串當作 `setTimeout` 或 `setInterval` 參數。當參數要被當成呼叫的函式時,這絕對是 **不好** 的程式碼,相反的,利用 *匿名函式* 來完成這樣的行為。 + +此外,應該避免使用 `setInterval`,因為他將不會被 Javascript 給中斷。 + +[1]: http://en.wikipedia.org/wiki/Document_Object_Model "Document Object Model" diff --git a/external/JavaScript-Garden/doc/zhtw/types/casting.md b/external/JavaScript-Garden/doc/zhtw/types/casting.md new file mode 100644 index 0000000..e6aa2cf --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/types/casting.md @@ -0,0 +1,62 @@ +## 類型轉換 + +JavaScript 是一個 *弱類型* 的程式語言,所以在 **任何** 情況下都可以 *強制類型轉換*。 + + // 這些都是真 + 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 + + // 下面都假 + 10 == 010; + 10 == '-10'; + +> **ES5 注意:** 如果數字字面值的開頭是 `0` 它會強制轉為八進位數字解析。 +> 而在 ES5 嚴格模式下,它已經被刪除了。 + +為了去避免上驗的事件發生,我們會用 [嚴格等於操作符](#types.equality) 這是強烈建議。 +因為它可以避免很多常見的問題,但 JavaScript 的弱類型系同仍然會導致一些其他問題。 + +### 內置類型的建構函式 + +內置類型(比如 `Number` 和 `String`)在被調用時,使用或不使用 `new` 的結果完全不同。 + + 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 + +使用內置類型 `Number` 作為建構函式會建造一個新的 `Number` 物件,而在不使用 `new` 關鍵字的 `Number` 函式更像是一個數字轉換器。 + +另外,在比較中引入物件的字面值會導致更加複雜的強制類型轉換。 + +最好的方式是比較值的 **顯示** 的轉換成最有可能的三種形態 + +### 轉換成字符串 + + '' + 10 === '10'; // true + +將一個值加上空字符串可以輕鬆轉為字符串類型。 + +### 轉換成一個數字 + + +'10' === 10; // true + +使用 **一元** 的加號操作符,可以把字符串轉為數字。 + +### 轉換成一個 Bool +通過使用 **否** 操作符兩字,可以把一個值轉換為 Bool。 + + !!'foo'; // true + !!''; // false + !!'0'; // true + !!'1'; // true + !!'-1' // true + !!{}; // true + !!true; // true + + diff --git a/external/JavaScript-Garden/doc/zhtw/types/equality.md b/external/JavaScript-Garden/doc/zhtw/types/equality.md new file mode 100644 index 0000000..1f20c6c --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/types/equality.md @@ -0,0 +1,60 @@ +## 相等與比較 + +JavaScript 有兩個不同的方式來比較兩個物件是否相等。 + +### 等於操作符 + +等於操作符是由兩個等號組成: `==` + +JavaScript 是一個 *弱類型* 語言。這代表它會為了比較兩個值而做 **強制類型轉換**。 + + "" == "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 + +上面的表格可以看出來這些結果強制轉換類型,這也代表說用 `==` 是一個不好的習慣,因為它會很難追蹤問題由於它複雜的規則。 + +此外,也有效率上面的問題在強制轉換類型。 +例如說一個字串會被轉成數字來和別的數字做比較。 + +### 嚴格等於操作符 + +不像普通的等於操作符 `===` 不會做強制類型轉換。 + + "" === "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 + +上面的結果比較清楚,也有利於程式碼的分析。如果這兩個操作數的類型不一樣都就不會相等,有助於它性能的提昇。 + +### 比較物件 + +雖然 `==` 和 `===` 都是等於操作符,但其中有一個操作數為物件時,它的行為就會不同。 + + {} === {}; // false + new String('foo') === 'foo'; // false + new Number(10) === 10; // false + var foo = {}; + foo === foo; // true + +在這裡等於操作符比較 **不是** 值的相等,而是否是 **相同** 的身分。 +有點像 Python 的 `is` 和 C 中的指標。 + +### 結論 + +強烈建議使用 **嚴格等於** +如果要轉換類型,應該要在 [explicitly](#types.casting)的時候轉換,而不是在語言本身用複雜的轉換規則。 + + diff --git a/external/JavaScript-Garden/doc/zhtw/types/instanceof.md b/external/JavaScript-Garden/doc/zhtw/types/instanceof.md new file mode 100644 index 0000000..fa5e8c3 --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/types/instanceof.md @@ -0,0 +1,32 @@ +## `instanceof` 操作符 + +`instanceof` 操作符用來比較兩個建構函數的操作數。只有在比較自定義的物件時才有意義。這和 [typeof operator](#types.typeof)一樣用處不大。 + +### 比較定意義物件 + + 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 + +### `instanceof` 比較內置類型 + + new String('foo') instanceof String; // true + new String('foo') instanceof Object; // true + + 'foo' instanceof String; // false + 'foo' instanceof Object; // false + +有一點需要注意的, `instanceof` 不會在來自不同的上下文的物件運作(例如:瀏覽器中不同的文檔結構),因為它的建構函數不一樣。 + +### In Conclusion + +`instanceof` 操作符應該 **只** 用來比較同一個 JavaScript 上下文定意義的物件。 +正如 [`typeof`](#types.typeof)操作符一樣,任何其他用法都要避免。 diff --git a/external/JavaScript-Garden/doc/zhtw/types/typeof.md b/external/JavaScript-Garden/doc/zhtw/types/typeof.md new file mode 100644 index 0000000..0a0908b --- /dev/null +++ b/external/JavaScript-Garden/doc/zhtw/types/typeof.md @@ -0,0 +1,74 @@ +## `typeof` 操作符 + +`typeof` 操作符 (和 +[`instanceof`](#types.instanceof)) 可能是最大的設計錯誤在 JavaScript,因為它幾乎不可能從它們那裡得到想要的結果。 + +雖然 `instanceof` 還是有一些限制上的使用, `typeof` 只有一個實際上的運用情形,但是 **不是** 用在檢查物件的類型。 + +> **注意:** 由於 `typeof` 也可以像函式的語法被調用,例如 `typeof(obj)`,但這並是一個函數調用。 +> 那兩個小括號只是用來計算一個表達式的值,這個返回值會作為 `typeof` 操作符的一個操作數。 +> 實際上 **不存在** 名為 `typeof` 的函式。 + + +### JavaScript 類型表格 + + 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 + +上面的表格中, *Type* 這一系列表示 `typeof` 的操作符的運算結果。可以看到,這個值的大多數情況下都返回物件。 + +*Class* 表示物件內部的屬性 `[[Class]]` 的值。 + + +> **JavaScript 標準文檔中定義:** `[[Class]]`的值只可能是下面字符串中的一個: +> `Arguments`, `Array`, `Boolean`, `Date`, `Error`, +> `Function`, `JSON`, `Math`, `Number`, `Object`, `RegExp`, `String` + +為了獲取對象的 `[[Class]]`,我們可以使用定義在 `Object.prototype` 上的方法 `toString`。 + +### 物件的類定義 + +JavaScript 標準文檔只給出了一種獲取 `[[Class]]` 值的方法,那就是使用 `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 + +上面的例子中,**`Object.prototype.toString` 用 [this](#function.this)的值來來調用被設置需要獲取 `[[Class]]` 值的物件。 + +> **ES5 Note:** 為了回傳 `Object.prototyp.toString` 值的方便 +> `null` 和 `undefined` 被 **改變** 從 `object` 到 `null` 和 `undefined` 在 ECMAScript 5。 + +### 測試未定義變數 + + typeof foo !== 'undefined' + +上面的例子確認 `foo` 是否真的被宣告。如果沒有定義會導致 `ReferenceError` 這是 `typeof` 唯一有用的地方 + +### 結語 + +為了去檢查一個物件,強烈建議去使用 `Object.prototype.toString` 因為這是唯一可以依賴的方式。 +正如上面所看到的 `typeof` 的事先返回值在標準文檔中未定義,因此不同的引擎可能不同。 + +除非為了檢測一個變數是否定義,我們應該避免使用 `typeof` 操作符。 + + diff --git a/external/JavaScript-Garden/es/index.html b/external/JavaScript-Garden/es/index.html new file mode 100644 index 0000000..2f3ded3 --- /dev/null +++ b/external/JavaScript-Garden/es/index.html @@ -0,0 +1,1410 @@ +Jardín de JavaScript

    Introducción

    Introducción

    El Jardín de JavaScript es una guía de documentación acerca de las +partes más peculiares de este lenguaje de programación. Brinda consejos para evitar +los errores más comunes y sutiles, así como problemas de rendimiento y de malas +prácticas que los programadores menos experimentados en JavaScript pueden resolver +en sus esfuerzos por profundizar en el lenguaje.

    +

    El Jardín de JavaScript no prentende enseñar JavaScript. +Se recomienda un conocimiento sobre el lenguaje para entender los temas tratados en +esta guía. Con el fin de aprender los conceptos básicos del lenguaje, por favor +diríjase a la excelente guía de los desarrolladores de Mozilla.

    +

    Los autores

    +

    Esta guía es el trabajo de dos encantadores usuarios del foro Stack Overflow, +Ivo Wetzel (Escrito) y Zhang Yi Jiang (Diseño).

    +

    Colaboradores

    + +

    Hosting

    +

    JavaScript Garden es hospedado en GitHub, además Cramer Development nos apoya +con un sitio espejo en JavaScriptGarden.info.

    +

    Licencia

    +

    El Jardín de JavaScript es publicado bajo la licencia MIT y es hospedado en +GitHub. Si encuentra algún error o errata por favor publique una incidencia o +envie un pull request a nuestro repositorio. También nos puede encontrar en la +sala de chat de JavaScript en Stack Overflow.

    +

    Objetos

    Uso de objetos y propiedades

    Todo en JavaScript actúa como un objeto, con las dos únicas excepciones de +null y undefined.

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

    Un error muy común es el uso de literales númericos como objetos. +Esto se debe a un error en el parser de JavaScript que intenta analizar la +notación de puntos como un literal de punto flotante.

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

    Existe un par de soluciones que pueden utilizarse para hacer que los +literales númericos actúen como objetos.

    +
    2..toString(); // el segundo punto es reconocido correctamente
    +2 .toString(); // observe el espacio a la izquierda del punto
    +(2).toString(); // el número 2 se evalúa primero
    +

    Objetos como un tipo de datos

    +

    Los objetos en JavaScript también pueden ser utilizados como una Tabla Hash o conocido como Hashmap en inglés, consisten +principalmente en nombres de propiedades, y asignándoles valores a éstas.

    +

    El uso de un objeto literal - con notación {} - puede crear un +objeto plano. Este nuevo objeto heredado desde Object.prototype +no posee propiedades propias definidas.

    +
    var foo = {}; // un nuevo objeto vacío
    +
    +// un nuevo objeto con la propiedad llamada 'test' con el valor 12
    +var bar = {test: 12};
    +

    Acceso a las propiedades

    +

    Se puede acceder a las propiedades de un objeto de dos maneras, ya sea a través de la +notación de punto o desde la notación de corchetes.

    +
    var foo = {name: 'kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // ¡funciona!
    +

    Ambas notaciones son idénticas en su funcionamiento, la única diferencia es la +notación de corchetes permite el ajuste dinámico de las propiedades, así como +el uso de propiedades que de otro modo daría lugar a error de sintaxis.

    +

    Eliminando propiedades

    +

    La única manera de eliminar una propiedad desde un objeto es usando el +operador delete; establecer la propiedad a undefined o null solamente +elimina el valor asociado a la propiedad, pero no la key (valor clave).

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

    Los resultados de la salida son bar undefined y foo null - sólo baz ha +sido removido y por lo tanto no aparece en la salida.

    +

    Notación de Keys

    +
    var test = {
    +    'case': 'Soy una palabra clave y debo ser anotado como string',
    +    delete: 'Soy una palabra clave también' // lanza SyntaxError
    +};
    +

    Las propiedades de los objetos puede ser simbolizados como caracteres planos y como strings. Debido +a otro mal diseño del parser de JavaScript, lo anterior es una excepción +de SyntaxError antes de ECMAScript 5.

    +

    Este error se produce porque delete es una keyword; por lo tanto, debe ser +anotado como un string literal para asegurarse que será interpretado correctamente +por diversos motores de JavaScript.

    +

    Prototipo

    JavaScript no posee en sus características un sistema clásico de herencia, sino que +utiliza un prototipo para esto.

    +

    Si bien a menudo se considera uno de los puntos débiles de JavaScript, el +modelo de herencia prototipado es de hecho más poderoso que el modelo clásico. +Por ejemplo, es bastante trivial construir un modelo clásico a partir del modelo prototipado, +mientras que al contrario es una tarea mucho más difícil.

    +

    Debido al hecho que JavaScript es básicamente el único lenguaje que utiliza +ampliamente la herencia prototipada, se necesita algo de tiempo para adaptarse a +las diferencias entre los dos modelos.

    +

    La primera gran diferencia es que la herencia en JavaScript se realiza usando +llamadas de cadenas de prototipo (prototype chains).

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Asigna el prototipo de Bar como una nueva instancia de Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Asegura que el constructor sea Bar
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // crea una nueva instancia de Bar
    +
    +// Resultado de cadena de prototipos (prototype chain)
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    En el código anterior, el objeto test hereda de Bar.prototype y Foo.prototype; +por lo tanto, tendrá acceso a la función method que se ha definido en Foo. +También se tendrá acceso a a la propiedad value de la única instancia de Foo +que compone su prototipo. Es importante tomar en cuenta que new Bar() no creará una nueva +instancia de Foo, pero retornará lo asignado en su prototipo; de este modo, todas las instancias +de Bar tendrán que compartir el mismo valor de la propiedad.

    + +

    Búsqueda de propiedades

    +

    Cuando se accede a las propiedades de un objeto, JavaScript recorre la cadena de +prototipo hacia arriba hasta encontrar la propiedad con el nombre solicitado.

    +

    Cuando se llega al final de la cadena - concretamente Object.prototype - y aún +no se ha encontrado la propiedad especificada, se retornará un valor +undefined en su lugar.

    +

    La propiedad prototype

    +

    Aunque la propiedad prototype es usada por el lenguaje para construir la cadena +de prototipos, es posible asignar cualquier valor. Aunque los tipos primitivos +serán ignorados cuando se asigne en prototype.

    +
    function Foo() {}
    +Foo.prototype = 1; // no tendrá efecto
    +

    La asignación de objetos, como se muestra en el ejemplo anterior, funcionará, y permitirá +la creación dinámica de cadena de prototipos.

    +

    Rendimiento

    +

    El tiempo tomado en la búsqueda de propiedades es alta y la cadena de prototipo puede +presentar un impacto negativo crítico en el rendimiento en partes del código. Además, +si ha tratado de acceder a propiedades que no existen, esto provoca que se recorra la cadena de prototipo completa.

    +

    Además, al recorrer en iteración las propiedades de un objeto +, cada propiedad encontrada en la cadena de prototipo será enumerada.

    +

    Extensión de prototipos nativos

    +

    Una mala característica que se suele utilizar para extender Object.prototype o cualquier +otro prototipo construido.

    +

    Esta técnica es conocida en inglés como monkey patching y rompe la encapsulación del código. +Si bien es utilizado en frameworks como Prototype, todavía no existen buenas razones para adoptarlo o integrarlo +como tipos de dato o como funcionalidad no estándar.

    +

    La única razón coherente para extender un prototipo es para adaptarle nuevas +características de los motores JavaScript más modernos; por ejemplo, +Array.forEach.

    +

    En conclusión

    +

    Se debe entender por completo el módelo de herencia prototipado antes de +escribir código complejo que lo utilice. Además, observe la longitud de la +cadena de prototipo y modifíquela si es necesario para evitar posibles problemas de +rendimiento. Con relación a los prototipos nativos, estos nunca deben ser extendidos a +menos que sea para mantener la compatibilidad con nuevas características de JavaScript.

    +

    hasOwnProperty

    Con el fin de comprobar si un objeto posee una propiedad definida en sí mismo y no +en algún lugar de su cadena de prototipo, es necesario utilizar +el método hasOwnProperty ya que todos los objetos herendan de Object.prototype.

    + +

    hasOwnProperty es la única utilidad en JavaScript que se ocupa de las propiedades +y no las salta en la cadena de prototipo.

    +
    // Envenenamiento en 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
    +

    Sólo hasOwnProperty retornará el resultado correcto y esperado, esto es +ensencial cuando se repite una iteración en las propiedades de cualquier objeto. No hay +otra maner de excluir las propiedades que no están definidas en el mismo objeto, pero +en alguna parte de su cadena de prototipo si.

    +

    hasOwnProperty como propiedad

    +

    JavaScript no protege el nombre de la propiedad hasOwnProperty; de este modo, si existe +la posibilidad de que un objeto tenga una propiedad con el mismo nombre, es necesario utilizar +hasOwnProperty como propiedad externa con el fin de obtener resultados correctos.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // siempre devolverá false
    +
    +// Utilice otro objeto con hasOwnProperty y llamelo con 'this' para asignarlo a foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    En conclusión

    +

    Cuando se necesite comprobar la existencia de una propiedad en un objeto, hasOwnProperty es +el único método para hacerlo. También se recomienda el uso de hasOwnProperty como +parte de un bucle for in, esto evitará errores desde +extenciones de prototipos nativos.

    +

    El bucle for in

    Al igual que el operador in, el bucle for in también recorre sobre la +cadena de prototipo cuando este se repite en una iteración en las propiedades de un objeto.

    + +
    // Envenenamiento en Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // Imprime ambos bar y moo
    +}
    +

    Dado que no es posible cambiar el comportamiento del bucle for in en sí mismo, es +necesario filtrar las propiedades internas no deseadas dentro del bucle, +esto se hace mediante el uso del método hasOwnProperty del +Object.prototype.

    + +

    Usando hasOwnProperty para filtrado

    +
    // Aún es el foo del código de arriba
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Está versión es la única forma correcta de uso. Esto se debe sólo al uso de +hasOwnProperty que imprimirá moo. Cuando hasOwnProperty se omita, el código es +propenso a errores en los casos de prototipos nativos - ej. Object.prototype - +se ha extendedido.

    +

    Uno de los frameworks más usado que implementa estas funcionalidades es Prototype. Cuando el +framework es incluido, el bucle for in que no utilicen hasOwnProperty no podrá garantizar que +se interrumpa.

    +

    En conclusión

    +

    Se recomienda utilizar siempre el uso de hasOwnProperty. Nunca debe suponer
    ningún entorno donde el código se ejecute, o si los prototipos +nativos han sido extendidos o no.

    +

    Funciones

    La declaración de funciones y expresiones

    Las funciones en JavaScript son funciones de primera clase (first class functions). +Esto significa que se pueden tratar como objetos. Un uso común de esta característica es pasar de +una función anónima a otra, posiblemente una función asíncrona. Esto se conoce como callback.

    +

    La declaración function

    +
    function foo() {}
    +

    La función anterior se carga así mismo antes de iniciar la ejecución del +programa; por lo tanto, está disponible en todo el scope (ámbito) de la aplicación +donde se ha definido, aunque hubiera sido llamado antes de definirse en el código.

    +
    foo(); // Funciona porque foo ha sido creado antes que este código se ejecute
    +function foo() {}
    +

    La expresión function

    +
    var foo = function() {};
    +

    Este ejemplo asigna una función sin nombre y anónima a la variable foo.

    +
    foo; // 'undefined'
    +foo(); // Lanza TypeError
    +var foo = function() {};
    +

    Debido a la declaración de var, que carga el nombre de la variable foo antes +de la ejecución real del inicio del código, foo ya estará definidido cuando se +ejecute el script.

    +

    Pero se asigna sólo si ocurre en tiempo de ejecución, el valor de foo de forma +predetermina es undefined antes de que el código se ejecute.

    +

    Expresión nombre de función

    +

    Otro caso especial de asignación de nombre de funciones.

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

    Aquí bar no está disponible en el ámbito externo (scope), ya que la función sólo es +asignada a foo; Sin embargo, dentro de bar si está disponible. Esto se debe a la forma +en como trabaja la resolución de nombres en JavaScript, el nombre de +la función esta siempre disponible en el ámbito local de la propia función.

    +

    Cómo trabaja this

    JavaScript tiene un concepto diferente sobre el nombre especial this referido a la +mayoría de lenguajes de programación. Hay exactamente cinco formas distintas en donde +es posible ver el valor de this dentro de lo posible en el lenguaje.

    +

    El ámbito global (Global Scope)

    +
    this;
    +

    Cuando se utiliza this en el ámbito global, simplemente se refiere al objeto global.

    +

    Llamar a una función

    +
    foo();
    +

    Aquí this se refiere al objeto global.

    + +

    Llamar a un método

    +
    test.foo(); 
    +

    En este ejemplo this se referiere a test.

    +

    Llamar a un constructor

    +
    new foo(); 
    +

    Llamar a una función que esta precedida por la palabra clave new actúa como +un constructor. Dentro de la función, this se refiere +al Objeto recién creado.

    +

    Ajuste explícito de this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // array que se apilará
    +foo.call(bar, 1, 2, 3); // resultados a = 1, b = 2, c = 3
    +

    Cuando se utiliza los métodos call o apply en Function.prototype, el valor de +this dentro de la función llamada se ajustará explícitamente al primer argumento +correspondiente a la llamada de la función.

    +

    Como resultado, el ejemplo anterior sobre los casos de métodos estos no se aplican, y this +dentro de foo puede establecerse en bar.

    + +

    Errores comunes

    +

    Si bien en la mayoría de los casos esto tiene sentido, el primero puede cosiderarse como otro +mal diseño del lenguaje, ya que nunca tiene un uso práctico.

    +
    Foo.method = function() {
    +    function test() {
    +        // this es establecido como un objeto global
    +    }
    +    test();
    +};
    +

    Un error común es que this dentro de test haga referencia a Foo, mientras que en +realidad esto no es así.

    +

    Con el fin de acceder a Foo desde dentro de test es necesario crear una variable local +dentro del método para referirse a Foo.

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

    that es justo un nombre normal, pero es comúnmente usado para referenciar a this +de forma externa. En combinación con closures, esto puede ser +también usado para pasar this como valor.

    +

    Asignación de métodos

    +

    Otra cosa que no funciona en JavaScript son los alias en las funciones, es decir, +asignar un método a una variable.

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

    Debido al primer caso, test actúa como una función de llamada; por lo que +this dentro de este no estará referido a someObject.

    +

    Mientras que la unión de this puede parecer una mala idea en un principio, esto es en +realidad lo que hace trabajar a la herencia de prototipo.

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

    Cuando los métodos son llamados desde una instancia de Bar, this se referirá a una +instancia.

    +

    Closures y referencias

    Una de las características más poderosas de JavaScript es la disponibilidad de closures (cerraduras), +esto significa que los ámbitos siempre podrán ser accedidos por ámbitos externos donde +fueron definidos. Dado que sólo el alcance es único en JavaScript en el +ámbito de la función, todas las funciones, por omisión, actúan como closures.

    +

    Emulando variables privadas

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

    En este caso, Counter retorna dos closures. La función increment y la +función get. Ambas funciones mantienen el ámbito de la referencia de
    Counter y, por lo tanto, siempre accede a la variable count que fue definido +en el ámbito.

    +

    ¿Por qué las variables privadas trabajan?

    +

    Dado que no es posible referenciar o asignar ámbitos en JavaScript, no hay +manera de acceder a la variable count desde fuera. Sólo existe una forma para +interactuar con estos vía los dos closures.

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

    El código anterior no ha cambiado la variable count en el ámbito de Counter, +desde foo.hack no es definido en ese ámbito. En su lugar se creará - o +se anulará - la variable global count.

    +

    Closures dentro de bucles

    +

    Un error frecuente en el uso de closures dentro de bucles, es como si se tratará +de copiar el valor del índice de la variable del bucle.

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

    El código anterior no tendrá como salida los números del 0 al 9, sino +simplementemente se imprimirá el número 10 diez veces.

    +

    La función anónima hace referencia a i y se llama a +console.log, el bucle for ya ha terminado y finalizo el valor de +i a 10.

    +

    Con el fin de obtener el comportamiento deseado, es necesario crear una copia +del valor de i.

    +

    Evitando el problema de referencia

    +

    Con el fin de copiar el valor de la variable índice del bucle, lo mejor es utilizar +un contenedor anónimo.

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

    La función anónima externa llamará inmediatamente a i como su primer +argumento y recibirá la copia del valor de i como parámetro de e.

    +

    La función anónima que se pasa a setTimeout ahora es una referencia a +e, cuyo valor no han sido cambiados por el bucle.

    +

    No hay otra manera de lograr esto; se debe retornar una función desde +el contenedor anónimo, que tendrá el mismo comportamiento que el código +anterior.

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

    El objeto arguments

    Cada ámbito de la función de JavaScript puede acceder a la variable especial arguments. +Está variable contiene una lista de todos los argumentos que se pasan a la función.

    + +

    El objeto arguments no es un Array. Si bien cuenta con la semántica +de un array - concretamente la propiedad length - no hereda de +Array.prototype y es de hecho un Objeto.

    +

    Debido a esto, no es posible usar los métodos estándar de los arrays como push, +pop o slice en arguments. Mientras que la iteración es un simple bucle for que +funciona muy bien, esto se convierte necesariamente en un Array real con el +fin de utilizar los métodos de un Array.

    +

    Conversión de un Array

    +

    El siguiente código devuelve un nuevo Array que contiene todos los elementos del +objeto arguments.

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

    Esta conversión es lenta, no es recomendable usarlo en puntos criticos que +afecten el rendimiento del código.

    +

    Pasar Argumentos

    +

    El siguiente método es recomendado para pasar argumentos desde una función a +otra.

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

    Otro truco es utilizar tanto call y apply juntos para crear contenedores rápidos y +consolidados.

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Crea una versión sin consolidar de "method" 
    +// Se toma los parámetros: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Resultado: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    Los parámetros formales y argumentos de índices

    +

    El objeto arguments crea las funciones de getter y setter para sus +propiedades, así como parámetros formales de la función.

    +

    Como resultado, se ha cambiado el valor formal del parámetro también se cambio el +valor de la propiedad correspondiente del objeto arguments, y al revés.

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

    Mitos y verdades sobre el rendimiento

    +

    El objeto arguments es siempre creado con las dos únicas excepciones cuando es +el caso en que declarado como un nombre dentro de la función o uno de los +parámetros formales. No importa si se utiliza o no.

    +

    Ambos getters y setters son siempre creados; por lo tanto, con que casi no se +tiene un impacto en el rendimiento en todo, especialemente no en el código real donde no +es más que un simple acceso a las propiedades del objeto arguments.

    + +

    Sin embargo, hay casos en que se reducirá drásticamente el rendimiento en los motores +modernos de JavaScript. Este es el caso del uso de arguments.callee.

    +
    function foo() {
    +    arguments.callee; // realiza algo con la función del objeto
    +    arguments.callee.caller; // y llama a la función del objeto
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Debería ser normalmente entre líneas...
    +    }
    +}
    +

    El código anterior, foo no puede estar sujeto a la expansión en línea ya que se +necesita saber acerca de sí mismo y la llamada. Esto no sólo denota los posibles beneficios +de rendimiento que surgen con la expansión en línea, ya que también interrumpe la encapsulación +ya que la función ahora puede ser dependiente de un contexto específico de llamada.

    +

    Es muy recomendable nunca hacer uso de arguments.callee o de cualquier +de sus propiedades.

    + +

    Constructores

    Los constructores en JavaScript todavía son diferentes a los de otros lenguajes. +Cualquier llamada que es precedida por la palabra new actua como un constructor.

    +

    Dentro del constructor - la función llama - el valor de this se refiere a un +Objeto recién creado. El prototipo de este nuevo +objeto se establece en el prototipo de la funcióno que es invocado como el +constructor.

    +

    Si la función que se llama no tiene una sentencia return explícita, entonces +implícitamente devuelve el valor de this - el nuevo objeto.

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

    La llamada de Foo por encima del constructor y establece el prototipo del objeto +recién creado a Foo.prototype.

    +

    En caso explícito de la sentencia return de la función devuelva el valor especificado +que la declaración, pero sólo si el valor devuelto es un Object.

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

    Cuando una nueva keyword es omitidad, la función no devuelve un nuevo objeto.

    +
    function Foo() {
    +    this.bla = 1; // se establece en el objeto global
    +}
    +Foo(); // undefined
    +

    Aunque el ejemplo anterior puede parecer que trabaja en algunos casos, debido +a los trabajos de this en JavaScript, que usará el +objeto global como valor de this.

    +

    Fábricas

    +

    Con el fin de ser capaz de omitir un nuevo keyword, la función del tiene +explícitamente devolver un valor.

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

    Ambos llamadas a Bar devuelven exactamente lo mismo, un reciente objeto creado que +tiene como propiedad llamada el method, esto es un +Closure.

    +

    También hay que notar que la llamada new Bar() no afecta al prototipo +del objeto devuelto. Mientras que el prototipo se establece en el objeto recién creado, + Bar nunca devuelve un nuevo objeto.

    +

    En el ejemplo anterior, no hay diferencia funcional entre usar y no usar +el keyword new.

    +

    Creación de nuevos objetos vía Factorias

    +

    Una recomendación a menudo es no utilizar new ya que su uso puede +conducir a errores.

    +

    Con el fin de crear un nuevo objeto, uno bien debe utilizar una fábrica y un +constructor para crear un nuevo objeto dentro de la fábrica.

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

    Aunque lo anterior es robuesto frente a la keyword new y, ciertamente hace +que el uso de variables privadas sea fácil, esto viene con +algunas desventajas.

    +
      +
    1. Se utiliza más memoria, ya que los objetos creados no comparten los métodos de +un prototipo.
    2. +
    3. Con el fin de heredar de una fábrica se necesita copiar todos los métodos a otro +objeto o poner todo en un prototipo de nuevo objeto.
    4. +
    5. La eliminación de una cadena de prototipo sólo por dejar la keyword new de +alguna manera va en contra del espíritu del lenguaje.
    6. +
    +

    En conclusión

    +

    Mientras que se omite el keyword new podría dar a errores, no es ciertamente +una razón para abandonar el uso de prototipos por completo. Al final todo se reduce a +la solución que se adapta mejor a las necesidades de la aplicación, especialmente si es +importante elegir un estilo específico en la creación de objetos +y resistirse.

    +

    Ámbitos y Namespaces

    A pesar que JavaScript tiene una muy buena sintaxis de dos llaves para los bloques, +está no es compatible con el soporte de ámbito de bloques; por lo que todo se deja +al lenguaje con el ámbito de la función.

    +
    function test() { // un ámbito
    +    for(var i = 0; i < 10; i++) { // no es un ámbito
    +        // cuenta
    +    }
    +    console.log(i); // 10
    +}
    + +

    Tampoco hay distintos namespaces en JavaScript, lo que significa que todo se define +en un namespace global y compartido.

    +

    Cada vez que una variable es referenciada, JavaScript recorre hacia arriba a través de todos +los ámbitos hasta encontrarlo. En este caso que llegue al ámbito global y todavía no ha +encontrado el nombre solicitado, se generará un error ReferenceError.

    +

    El terror de las variables globales

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

    Estos dos scripts no tienen el mismo efecto. El script A define una variable +llamada foo en el ámbito global y el script B define foo en el +actual ámbito.

    +

    Una vez más, esto no tiene el mismo efecto para todo, no usar var puede tener +mayor implicación.

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

    Dejando de lado la sentencia var dentro de la función test sobre escribiría el +valor de foo. Si bien al principio puede parecer un gran cambio, se tiene +miles de líneas de código en JavaScript y no se usaría var introduciendose en un +horrible y difícil detección de errores.

    +
    // ámbito global
    +var items = [/* some list */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // ámbito de subLoop
    +    for(i = 0; i < 10; i++) { // falta la sentencia var
    +        // ¡realizar cosas asombrosas!
    +    }
    +}
    +

    El bucle externo terminará después de la primera llamada a subLoop, desde subLoop +sobreescribe el valor global de i. Usando var para el segundo bucle for se hace +fácil evitar este error. La sentencia var no debe nunca dejarse a menos que +el efecto deseado es afectado por el ámbito exterior.

    +

    Variables locales

    +

    La única fuente para las variables locales en JavaScript son los parámetros de la +función y variables que fueron declaradas vía la sentencia +var.

    +
    // ámbito global
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // ámbito local de la función test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    Mientras foo y i son variables locales dentro del ámbitor de la función test, +ela asignación de bar sobreescribe la variable global con el mismo nombre.

    +

    Hoisting

    +

    La declaración de hoists en JavaScript. Esto significa que tanto la declaración de var y +la función declarada se translada a la parte superior de su ámbito que lo contiene.

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

    El código anterior transforma antes de ejecutarse. JavaScript mueve +la declaracione var aspi como las declaraciones de la función a la parte superior a +lo más cercano del ámbito circundante.

    +
    // declaraciones var movidas aquí
    +var bar, someValue; // por omisión 'undefined'
    +
    +// la función declarada es movida aquí también
    +function test(data) {
    +    var goo, i, e; // se pierde el ámbito del bloque movido aquí
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // falla con TypeError desde bar sigue en 'undefined'
    +someValue = 42; // las asignaciones no se ven afectadas por hoisting
    +bar = function() {};
    +
    +test();
    +

    La falta de alcance del bloque no sólo moverá la declaración var fuera de los bucles y +su contenido, sino también hará que los resultados de ciertos constructores if +no sean intuitivas.

    +

    En el código original la declaración de if si parecía modificar la variable +global goo, mientras actualmente este modifica la variable local - después hoisting +ha sido aplicado.

    +

    Sin el conocimiento acerca de hoisting, a continuación el código puede parecer +un ReferenceError.

    +
    // comprueba si SomeImportantThing ha iniciado
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Pero, por supuesto, lo anterior funciona debido a que la declaración var es movida +a la parte superior del ámbito global.

    +
    var SomeImportantThing;
    +
    +// otro código podría iniciar SomeImportantThing aqui, o no
    +
    +// asegúrese de que está ahí
    +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, but 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.

    +

    Arrays

    Iteración de un Array y sus propiedades

    A pesar que los arrays en JavaScript son objetos, no existe un buena razón para +usarlo en un bucle for para una interación de este. De +hecho, hay un número de buenas razones contra el uso de for in en arrays.

    + +

    Dado que el bucle for in enumera todas las propiedades que están en una cadena +de prototipo y la única manera para excluir estas propiedades es el uso de +hasOwnProperty, ya que es veinte veces más +lento que un bucle for normal.

    +

    Iteración

    +

    Con el fin de obtener el mejor rendimiento cuando se repite la interación de arrays, +es lo mejor hacer uso del clásico bucle for.

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

    Hay una captura adicional en el ejemplo anterior, que es el almacenamiento de la +caché de longitud del array vía l = list.length.

    +

    Aunque la propiedad length es definida en el mismo array, todavía posee una sobrecarga +para realizar la búsqueda en cada interación del bucle. Y mientras que los últimos +motores de JavaScript pueden aplicar optimizaciones en este caso, no hay manera +de saber si el ćodigo se ejecutará en uno de estos nuevos motores nuevos o no.

    +

    De hecho, dejando de lado el almacenamiento en caché puede resultar que el bucle +inicie sólo la mitad de rápido que con la longitud de la caché.

    +

    La propiedad length

    +

    Mientras que getter de la propiedad length simplemente retorne el número de +elementos son contenidos en un array, el setter puede ser usado para +truncar el array.

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

    La asignación de un menor número de longitud trunca al array, pero incrementando la +longitud no tiene ningún efecto sobre el array.

    +

    En conclusión

    +

    Para obtener el mejor rendimiento es recomendable siempre usar el bucle for +y alamacenar en caché la propiedad length. El uso del bucle for in en un array +es señal de un código mal escrito propenso a errores y un mal desempeño.

    +

    El constructor Array

    Desde el constructor Array es ambiguo en la forma en que ocupa sus párametros, +es recomendable siempre el uso de arrays literales - la notación [] - +cuando se crean nuevos arrays.

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

    En casos cuando sólo hay un argumento pasado al constructor del Array, +y que el argumento es un Número, el contructor devolverá un array disperso +con la propiedad length establecida al valor del argumento. Esto debe señalarse +que la propiedad length sólo del nuevo array se establecerá de esa manera, +los índices reales de la matriz no se iniciará.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // falso, el índice no se ha establecido
    +

    El comportamiento de poder establecer la longitud de un array inicial sólo es útil +en algunos casos array, como la repetición de una cadena, en la que se evita el uso +del código de bucle for.

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

    En conclusión

    +

    El uso de un constructor Array debe ser devuelto como sea posible. +Los literales son definitivamente preferidos. Estos son más cortos y tienen una +sintaxis más limpia; por lo tanto, también se incrementa la legibilidad del código.

    +

    Tipos

    Igualdad y Comparación

    JavaScript posee 2 maneras diferentes para comparar valores entre objetos para comprobar igualdad.

    +

    El Operador de Igualdad

    +

    El operador de igualdad consiste en 2 signos es igual: ==

    +

    JavaScript utiliza tipado débil. Esto significa que el operador de igualdad +obliga una conversión de tipos para poder compararlos.

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

    La tabla anterior muestra los resultados de la conversión de tipos, éste es el motivo principal +de por qué el uso de == es ampliamente considerado una mala práctica. Introduce errores +difíciles de identificar debido a la complejidad de sus reglas de conversión.

    +

    Además, existe un impacto en el rendimiento cuando entra en juego la conversión de tipos; +por ejemplo, una cadena debe ser convertida a número antes de poder ser comparada +con otro número.

    +

    El Operador de Igualdad Estricto

    +

    El operador de igualdad estricto consiste en tres signos es igual: ===:

    +

    Funciona exactamente igual que el operador de igualdad, excepto que el operador de igualdad +estricto no utiliza conversión de tipos entre sus operandos.

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

    Los resultados anteriores son mucho más claros y permiten una detección de errores temprana. +Esto permite un código más sólido en cierto grado y también mejora el rendimiento +en el caso que los operandos sean de tipos diferentes.

    +

    Comparando Objetos

    +

    Aunque == como === son considerados operadores de igualdad, se comportan +de maneras diferentes cuando al menos uno de sus operandos es Object.

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

    En este caso, los dos operadores comparan por referencia y no por igualdad; esto es, +comparan por la misma instancia del objeto, parecido +al operador is en Python y la comparación entre punteros en C.

    +

    En Conclusión

    +

    Es altamente recomendable usar sólo el operador de igualdad estricta. En los casos +donde los tipos de datos necesitan ser convertidos, debe hacerse explícitamente +y no dejárselo a las complicadas reglas de conversión del lenguaje.

    +

    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 because 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 originate 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 originate 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 an 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
    +

    Núcleo

    ¿Por qué no usar eval?

    La función eval ejecuta un string como código JavaScript en el ámbito local.

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

    Pero eval sólo ejecutará en ámbito local cuando es llamado directamente y +el nombre de la función llamada es eval.

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

    El uso de eval debe evitarse a toda costa. El 99.9% de su "uso" puede +lograrse sin su uso..

    +

    eval disfrazado

    +

    Las funciones de tiempo de espera setTimeout y setInterval pueden +tomar un string como primer argumento. En este caso, el string siempre se ejecutará en +el ámbito global ya que eval no ha sido llamado directamente.

    +

    Problemas de seguridad

    +

    eval es también un problema de seguridad ya que ejecuta cualquier código enviado, +y nunca debe usarse con strings que no se conozcan o tengan un origen no confiable.

    +

    En conclusión

    +

    eval nunca debe ser usado, cualquier código que haga uso del mismo debe ser cuestionado +en su funcionamiento, rendimiento y seguridad. En caso de que se necesite trabajar con +eval, el diseño ha de ser cuestionado y no debe utilizarse en primer lugar, se +debe usar un mejor diseño, que no requiera el uso de eval.

    +

    undefined y null

    JavaScript tiene dos valores distintos para nothing, el más útil de estos dos +es undefined.

    +

    El valor undefined

    +

    undefined es un tipo de dato con exactamente el mismo valor: undefined.

    +

    El lenguaje también define una variable global que tiene el valor de undefined, +Esta variable es también llamada undefined. Sin embargo, esta variable no es una +constante, ni es una palabra reservada del lenguaje. Esto significa que el valor +puede ser sobreescrito fácilmente.

    + +

    Algunos ejemplos cuando el valor retorna undefined:

    +
      +
    • Acceso a la variable global (sin modificar) undefined.
    • +
    • Retorna implícitamente las funciones que no posean la sentencia return.
    • +
    • Sentencia return que no retorna nada de forma explicíta.
    • +
    • Búsquedas de propiedades inexistentes.
    • +
    • Párametros de la función que no tienen ningún valor explicíto pasado.
    • +
    • Cualquier valor que se estable en undefined.
    • +
    +

    Manejar los cambios en el valor deChanges undefined

    +

    Dado que la variable undefined sólo tiene una copia del value de
    undefined, assigna un nuevo valor que no cambie el valor del +tipo undefined.

    +

    Aún con el fin de comparar con el valor de undefined es necesario +recuperar el valor de undefined primero.

    +

    Con el fin de proteger una posible sobreescritura en la variable undefined, +una técnica común es agregar un párametro adicional a un +wrapper anónimo, que consiga ningún párametro que se le pase.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined en el ámbito local
    +    // ahora hace referencia al valor
    +
    +})('Hello World', 42);
    +

    Otra forma de lograrlo un mismo efecto es declarar dentro un +wrapper.

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

    La única diferencia, es que está versión es de 4 bytes más que utiliza, y en +caso se comprima y no hay otra declaración de 'var' dentro del +wrapper anónimo.

    +

    Uso de null

    +

    Mientras que undefined en el contexto del lenguaje JavaScript es muy usado +en el sentido tradicional como null, el actual null (ambos literal y de un tipo) +es más o menos que otro tipo de datos.

    +

    Es utilizado en algunos detalles internos de JavaScript (como declarar al final de un +cadena de prototipo estableciendo Foo.prototype = null), pero en casi todos los +casos, puede ser reemplazado por undefined.

    +

    Automatic Semicolon Insertion

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

    +

    JavaScript is not a semicolon-less language. In fact, it 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 because 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, but they will also prevent the +JavaScript parser from changing its behavior.

    +

    Otros

    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, which 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 solution, 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

    +

    Because 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 because 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(1, 2, 3);
    +}, 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.

    +

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

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/favicon.ico b/external/JavaScript-Garden/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..299981896f63a15d15ad3a129e1c4a5f3a47263a GIT binary patch literal 375 zcmZ?wbhEHbRA5kGIKsei`1B>Oq)Hj}T2w@qL6|NnnpJs$>AfZ|UUuyP#`39^%c^^1Z^UrOe@j8*G$UhgZ=Isg2D zMTwM+e;(h2SbnB2s&cGM?-x{X)OgLCa{Pm%0Q<_sz%6o>ZAlNFaZHh5VR$fMg``|B zQ_zzMk6f64eqpSb^oom#!zQiNrd*(^x<-vVO@N!Dq)dq?nAJBSR;!J_JaFD*_Bx%p z>$@x&hg60GObw2gJ}@+#=IRz;um%7mpMnek literal 0 HcmV?d00001 diff --git a/external/JavaScript-Garden/fi/index.html b/external/JavaScript-Garden/fi/index.html new file mode 100644 index 0000000..56798c2 --- /dev/null +++ b/external/JavaScript-Garden/fi/index.html @@ -0,0 +1,1018 @@ +JavaScript-puutarha

    Johdanto

    Oliot

    Olioiden käyttö ja ominaisuudet

    Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä oliomaisesti. Nämä poikkeukset ovat null sekä undefined.

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

    Yleisesti luullaan ettei numeroliteraaleja voida käyttää olioina. Tämä johtuu viasta JavaScriptin parserissa. Se yrittää parsia numeron pistenotaatiota liukulukuliteraalina.

    +
    2.toString(); // palauttaa SyntaxError-virheen
    +

    Tämä voidaan välttää esimerkiksi seuraavasti.

    +
    2..toString(); // toinen piste tunnistuu oikein
    +2 .toString(); // huomaa pisteen vasemmalla puolen oleva väli
    +(2).toString(); // 2 arvioidaan ensi
    +

    Oliot tietotyyppinä

    +

    JavaScriptin olioita voidaan käyttää myös hajautustauluna, koska ne muodostavat pääasiassa avaimien ja niihin liittyvien arvojen välisen mappauksen.

    +

    Olioliteraalinotaatiota - {} - käyttäen voidaan luoda tyhjä olio. Tämä olio perii Object.prototype-olion eikä sille ole määritelty omia ominaisuuksia.

    +
    var foo = {}; // uusi, tyhjä olio
    +
    +// uusi, tyhjä olio, joka sisältää ominaisuuden 'test' arvolla 12
    +var bar = {test: 12}; 
    +

    Pääsy ominaisuuksiin

    +

    Olion ominaisuuksiin voidaan päästä käsiksi kahta eri tapaa käyttäen. Siihen voidaan käyttää joko piste- tai hakasulkunotaatiota.

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

    Kumpikin notaatio toimii samalla tavoin. Ainut ero liittyy siihen, että hakasulkunotaation avulla ominaisuuksien arvoja voidaan asettaa dynaamisesti. Se sallii myös muuten hankalien, virheeseen johtavien nimien käyttämisen.

    +

    Ominaisuuksien poistaminen

    +

    Ainut tapa poistaa olion ominaisuus on käyttää delete-operaattoria. Ominaisuuden asettaminen joko arvoon undefined tai null poistaa vain siihen liittyneen arvon muttei itse avainta.

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

    Yllä oleva koodi tulostaa sekä both undefined että foo null. Ainoastaan baz on poistettu. Täten sitä ei myöskään näy tulosteessa.

    +

    Avainnotaatio

    +
    var test = {
    +    'case': 'Olen avainsana, joten minun tulee olla merkkijono',
    +    delete: 'Myös minä olen avainsana' // palauttaa SyntaxError-virheen
    +};
    +

    Olioiden ominaisuuksia voidaan notatoida käyttäen joko pelkkiä merkkejä tai merkkijonoja. Toisesta JavaScriptin suunnitteluvirheestä johtuen yllä oleva koodi palauttaa SyntaxError-virheen ECMAScript 5:ttä edeltävissä versioissa.

    +

    Tämä virhe johtuu siitä, että delete on avainsana. Täten se tulee notatoida merkkijonona. Tällöin myös vanhemmat JavaScript-tulkit ymmärtävät sen oikein.

    +

    Prototyyppi

    JavaScript ei sisällä klassista perintämallia. Sen sijaan se käyttää prototyyppeihin pohjautuvaa ratkaisua.

    +

    Usein tätä pidetään JavaScriptin eräänä suurimmista heikkouksista. Itse asiassa prototyyppipohjainen perintämalli on voimakkaampi kuin klassinen malli. Sen avulla voidaan mallintaa klassinen malli melko helposti. Toisin päin mallintaminen on huomattavasti vaikeampaa.

    +

    JavaScript on käytännössä ainut laajasti käytetty kieli, joka tarjoaa tuen prototyyppipohjaiselle perinnälle. Tästä johtuen mallien väliseen eroon tottuminen voi viedä jonkin akaa.

    +

    Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä se pohjautuu erityisiin prototyyppiketjuihin.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Aseta Barin prototyypin uuteen Foo-olioon
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Terve maailma';
    +
    +// Huolehdi siitä, että Bar on todellinen konstruktori
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // luo uusi bar
    +
    +// Prototyyppiketju
    +test [Bar-olio]
    +    Bar.prototype [Foo-olio] 
    +        { foo: 'Terve maailma', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* jne. */ }
    +

    Yllä olio test perii sekä Bar.prototype- että Foo.prototype-olion. Tällöin se pääsee käsiksi Foo:ssa määriteltyy funktioon method. Se pääsee käsiksi myös ominaisuuteen value, jonka luotu Foo-olio sisältää prototyypissään. On tärkeää huomata, että new Bar() ei luo uutta Foo-oliota vaan käyttää uudelleen sen prototyyppiin asetettua. Tässä tapauksessa kaikki Bar-oliot jakavat siis saman value-ominaisuuden.

    + +

    Ominaisuushaut

    +

    Kun olion ominaisuuksien arvoa haetaan, JavaScript käy prototyyppiketjua läpi ylöspäin, kunnes se löytää ominaisuuden nimeä vastaavan arvon.

    +

    Jos se saavuttaa ketjun huipun - Object.prototype-olion - eikä ole vieläkään löytänyt haettua ominaisuutta, se palauttaa undefined arvon sen sijaan.

    +

    Prototyyppi-ominaisuus

    +

    Vaikka Prototyyppi-ominaisuutta käytetään prototyyppiketjujen rakentamiseen, voidaan siihen asettaa mikä tahansa arvo. Mikäli arvo on primitiivi, se yksinkertaisesti jätetään huomiotta.

    +
    function Foo() {}
    +Foo.prototype = 1; // ei vaikutusta
    +

    Kuten esimerkissä yllä, prototyyppiin on mahdollista asettaa olioita. Tällä tavoin prototyyppiketjuja voidaan koostaa dynaamisesti.

    +

    Suorituskyky

    +

    Prototyyppiketjussa korkealla olevien ominaisuuksien hakeminen voi hidastaa koodin kriittisiä osia. Tämän lisäksi olemattomien ominaisuuksien hakeminen käy koko ketjun läpi.

    +

    Ominaisuuksia iteroidessa prototyyppiketjun jokainen ominaisuus käydään läpi.

    +

    Natiivien prototyyppien laajentaminen

    +

    JavaScript mahdollistaa Object.prototype-olion sekä muiden natiivityyppien laajentamisen.

    +

    Tätä tekniikkaa kutsutaan nimellä apinapätsäämiseksi. Se rikkoo kapseloinnin. Vaikka yleisesti käytetyt alustat, kuten Prototype, käyttävätkin sitä, ei ole olemassa yhtään hyvää syytä, minkä takia natiivityyppejä tulisi laajentaa epästandardilla* toiminnallisuudella.

    +

    Ainut hyvä syy on uudempien JavaScript-tulkkien sisältämien ominaisuuksien siirtäminen vanhemmille alustoille. Eräs esimerkki tästä on Array.forEach.

    +

    Yhteenveto

    +

    Ennen kuin kirjoitat monimutkaista prototyyppiperintää hyödyntävää koodia, on olennaista, että ymmärrät täysin kuinka se toimii. Ota huomioon myös prototyyppiketjujen pituus ja riko niitä tarpeen mukaan välttääksesi suorituskykyongelmia. Huomioi myös, että natiiveja prototyyppejä ei tule laajentaa milloinkaan ellei kyse ole vain yhteensopivuudesta uudempien JavaScript-ominaisuuksien kanssa.

    +

    hasOwnProperty

    Jotta voimme tarkistaa onko olion ominaisuus määritelty siinä itsessään, tulee käyttää erityistä Object.prototype-oliosta periytyvää hasOwnProperty-metodia. Tällä tavoin vältämme prototyyppiketjun sisältämät ominaisuudet.

    + +

    hasOwnProperty on ainut JavaScriptin sisältämä metodi, joka käsittelee ominaisuuksia eikä käy prototyyppiketjun sisältöä läpi.

    +
    // Object.prototypen myrkyttäminen
    +Object.prototype.bar = 1; 
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // tosi
    +
    +foo.hasOwnProperty('bar'); // epätosi
    +foo.hasOwnProperty('goo'); // tosi
    +

    Ainoastaan hasOwnProperty palauttaa oikean ja odotetun tuloksen. Sen tietäminen on olennaista minkä tahansa olion ominaisuuksia iteroidessa. Tämä on ainut tapa löytää olion itsensä ominaisuudet prototyyppiketjusta riippumatta.

    +

    hasOwnProperty ominaisuutena

    +

    JavaScript ei suojele hasOwnProperty-metodin nimeä. Täten on mahdollista, että olio voi sisältää samannimisen ominaisuuden. Jotta voimme saada oikeita tuloksia, tulee sen sijaan käyttää ulkoista hasOwnProperty-metodia.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Olkoon vaikka lohikäärmeitä'
    +};
    +
    +foo.hasOwnProperty('bar'); // palauttaa aina epätoden
    +
    +// Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla
    +// 'this' foohon
    +({}).hasOwnProperty.call(foo, 'bar'); // tosi
    +

    Yhteenveto

    +

    Mikäli pitää selvittää kuuluuko ominaisuus olioon vai ei, ainoastaan hasOwnProperty voi kertoa sen. Tämän lisäksi on suositeltavaa käyttää hasOwnProperty-metodia osana jokaista for in-luuppia. Tällä tavoin voidaan välttää natiivien prototyyppien laajentamiseen liittyviä ongelmia.

    +

    for in-luuppi

    Aivan kuten in-operaattori, myös for in-luuppi käy olion prototyyppiketjun läpi iteroidessaan sen ominaisuuksia.

    + +
    // Object.prototypen myrkyttäminen
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // tulostaa sekä bar että moo
    +}
    +

    Koska for in-luupin käytöstapaa ei voida muokata suoraan, tulee ei-halutut ominaisuudet karsia itse luupin sisällä. Tämä on mahdollista käyttäen Object.prototype-olion hasOwnProperty-metodia.

    + +

    hasOwnProperty-metodin käyttäminen karsimiseen

    +
    // foo kuten yllä
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Tämä versio on ainut oikea. Se tulostaa ainoastaan moo, koska se käyttää hasOwnProperty-metodia oikein. Kun se jätetään pois, on koodi altis virheille tapauksissa, joissa prototyyppejä, kuten Object.prototype, on laajennettu.

    +

    Prototype on eräs yleisesti käytetty ohjelmointialusta, joka tekee näin. Kun kyseistä alustaa käytetään, for in-luupit, jotka eivät käytä hasOwnProperty-metodia, menevät varmasti rikki.

    +

    Yhteenveto

    +

    On suositeltavaa käyttää aina hasOwnProperty-metodia. Ei ole kannattavaa tehdä ajoympäristöön tai prototyyppeihin liittyviä oletuksia.

    +

    Funktiot

    Funktiomääreet ja lausekkeet

    JavaScriptissä funktiot ovat ensimmäisen luokan olioita. Tämä tarkoittaa sitä, että niitä voidaan välittää kuten muitakin arvoja. Usein tätä käytetään takaisinkutsuissa käyttämällä nimettömiä, mahdollisesti asynkronisia funktioita.

    +

    function-määre

    +
    function foo() {}
    +

    Yllä oleva funktio hilataan ennen ohjelman suorituksen alkua. Se näkyy kaikkialle näkyvyysalueessaan, jossa se on määritelty. Tämä on totta jopa silloin, jos sitä kutsutaan ennen määrittelyään.

    +
    foo(); // Toimii, koska foo on luotu ennen kuin koodi suoritetaan
    +function foo() {}
    +

    function-lauseke

    +
    var foo = function() {};
    +

    Tämä esimerkki asettaa nimeämättömän ja nimettömän funktion muuttujan foo arvoksi.

    +
    foo; // 'undefined'
    +foo(); // tämä palauttaa TypeError-virheen
    +var foo = function() {};
    +

    var on määre. Tästä johtuen se hilaa muuttujanimen foo ennen kuin itse koodia ryhdytään suorittamaan.

    +

    Sijoituslauseet suoritetaan vasta kun niihin saavutaan. Tästä johtuen foo saa arvokseen undefined ennen kuin varsinaista sijoitusta päästään suorittamaan.

    +

    Nimetty funktiolauseke

    +

    Nimettyjen funktioiden sijoitus tarjoaa toisen erikoistapauksen.

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

    Tässä tapauksessa bar ei ole saatavilla ulommalla näkyvyysalueessa. Tämä johtuu siitä, että se on sidottu foo:n sisälle. Tämä johtuu siitä, kuinka näkyvyysalueet ja niihin kuuluvat jäsenet tulkitaan. Funktion nimi on aina saatavilla sen paikallisessa näkyvyysalueessa itsessään.

    +

    Kuinka this toimii

    JavaScripting this toimii eri tavoin kuin useimmissa kielissä. Tarkalleen ottaen on olemassa viisi eri tapaa, joiden mukaan sen arvo voi määrittyä.

    +

    Globaali näkyvyysalue

    +
    this;
    +

    Kun this-muuttujaa käytetään globaalissa näkyvyysalueessa, viittaa se globaaliin olioon.

    +

    Funktiokutsu

    +
    foo();
    +

    Tässä tapauksessa this viittaa jälleen globaaliin olioon.

    + +

    Metodikutsu

    +
    test.foo(); 
    +

    Tässä esimerkissä this viittaa test-olioon.

    +

    Konstruktorikutsu

    +
    new foo(); 
    +

    Funktiokutsu, jota edeltää new-avainsana toimii konstruktorina. Funktion sisällä this viittaa juuri luotuun Object-olioon.

    +

    this-arvon asettaminen

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // taulukko laajenee alla olevaksi
    +foo.call(bar, 1, 2, 3); // tuloksena a = 1, b = 2, c = 3
    +

    Function.prototype-olion call- ja apply-metodeita käytettäessä this-ominaisuuden arvo määrittyy ensimmäisen annetun argumentin perusteella.

    +

    Seurauksena foo-funktion sisältämä this asettuu bar-olioon toisin kuin perustapauksessa.

    + +

    Yleisiä ongelmakohtia

    +

    Useimmat näistä tapauksista ovat järkeviä. Ensimmäistä niistä tosin voidaan pitää suunnitteluvirheenä, jolle ei ole mitään järkevää käyttöä ikinä.

    +
    Foo.method = function() {
    +    function test() {
    +        // this asettuu globaaliin olioon
    +    }
    +    test();
    +};
    +

    Yleisesti luullaan, että test-funktion sisältämä this viittaa tässä tapauksessa Foo-olioon. Todellisuudessa se ei kuitenkaan tee näin.

    +

    Jotta Foo-olioon voidaan päästä käsiksi test-funktion sisällä, tulee metodin sisälle luoda paikallinen muuttuja, joka viittaa Foo-olioon.

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Käytä thatia thissin sijasta
    +    }
    +    test();
    +};
    +

    that on normaali nimi, jota käytetään yleisesti viittaamaan ulompaan this-muuttujaan. Sulkeumia käytettäessä this-arvoa voidaan myös välittää edelleen.

    +

    Metodien sijoittaminen

    +

    JavaScriptissä funktioita ei voida nimetä uudelleen eli siis sijoittaa edelleen.

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

    Ensimmäisestä tapauksesta johtuen test toimii kuten normaali funktiokutsu; tällöin sen sisältämä this ei enää osoita someObject-olioon.

    +

    Vaikka this-arvon myöhäinen sidonta saattaa vaikuttaa huonolta idealta, se mahdollistaa prototyyppeihin pohjautuvan perinnän.

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

    Kun method-metodia kutsutaan Bar-oliossa, sen this viittaa juurikin tuohon olioon.

    +

    Sulkeumat ja viitteet

    Sulkeumat ovat eräs JavaScriptin voimakkaimmista ominaisuuksista. Näkyvyysalueilla on siis aina pääsy ulompaan näkyvyysalueeseensa. Koska JavaScriptissä ainut tapa määritellä näkyvyyttä pohjautuu funktionäkyvyyteen, kaikki funktiot käyttäytyvät oletuksena sulkeumina.

    +

    Paikallisten muuttujien emulointi

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

    Tässä tapauksessa Counter palauttaa kaksi sulkeumaa. Funktion increment lisäksi palautetaan myös funktio get. Kumpikin funktio viittaa Counter-näkyvyysalueeseen ja pääsee siten käsiksi count-muuttujan arvoon.

    +

    Miksi paikalliset muuttujat toimivat

    +

    JavaScriptissä ei voida viitata näkyvyysalueisiin. Tästä seuraa ettei count-muuttujan arvoon voida päästä käsiksi funktion ulkopuolelta. Ainoastaan nämä kaksi sulkeumaa mahdollistavat sen.

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

    Yllä oleva koodi ei muuta muuttujan count arvoa Counter-näkyvyysalueessa. Tämä johtuu siitä, että foo.hack-ominaisuutta ei ole määritelty kyseisessä näkyvyysalueessa. Sen sijaan se luo - tai ylikirjoittaa - globaalin muuttujan count.

    +

    Sulkeumat luupeissa

    +

    Usein sulkeumia käytetään väärin luuppien sisällä indeksimuuttujien arvon kopiointiin.

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

    Yllä oleva koodi ei tulosta numeroita nollasta yhdeksään. Sen sijaan se tulostaa numeron 10 kymmenen kertaa.

    +

    Nimetön funktio saa viitteen i-muuttujaan console.log-kutsuhetkellä. Tällöin luuppi on jo suoritettu ja i:n arvoksi on asetettu 10.

    +

    Päästäksemme haluttuun lopputulokseen on tarpeen luoda kopio i:n arvosta.

    +

    Viiteongelman välttäminen

    +

    Voimme välttää ongelman käyttämällä nimetöntä käärettä.

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

    Nimetöntä ulkofunktiota kutsutaan heti käyttäen i:tä se ensimmäisenä argumenttina. Tällöin se saa kopion i:n arvosta parametrina e.

    +

    Nimetön funktio, jolle annetaan setTimeout sisältää nyt viitteen e:hen, jonka arvoa luuppi ei muuta.

    +

    Samaan lopputulokseen voidaan päästä myös palauttamalla funktio nimettömästä kääreestä. Tällöin se käyttäytyy samoin kuten yllä.

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

    arguments-olio

    Jokainen JavaScriptin näkyvyysalue pääsee käsiksi erikoismuuttujaan nimeltään arguments. Tämä muuttuja sisältää listan kaikista funktiolle annetuista argumenteista.

    + +

    arguments-olio ei ole Array. Sen semantiikka, erityisesti length-ominaisuus, muistuttaa taulukkoa. Tästä huolimatta se ei peri Array.prototype:stä ja on itse asiassa Object.

    +

    Tästä johtuen arguments-olioon ei voida soveltaa normaaleja taulukkometodeja, kuten push, pop tai slice. Vaikka iterointi onnistuukin for-luuppeja käyttäen, tulee se muuttaa aidoksi Array-olioksi ennen kuin siihen voidaan soveltaa näitä metodeja.

    +

    Array-olioksi muuttaminen

    +

    Alla oleva koodi palauttaa uuden Array-olion, joka sisältää arguments-olion kaikki jäsenet.

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

    Tämä muutos on luonteeltaan hidas eikä sitä suositella käytettävän suorituskykyä vaativissa osissa koodia.

    +

    Argumenttien antaminen

    +

    Funktiosta toiselle voidaan antaa argumentteja seuraavasti.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // tee jotain
    +}
    +

    Toinen keino on käyttää sekä call- että apply-funktioita yhdessä ja luoda nopeita, sitomattomia kääreitä.

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Luo "metodin" sitomaton versio 
    +// Se ottaa seuraavat parametrit: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Tulos: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    Muodolliset parametrit ja argumenttien indeksit

    +

    arguments-olio luo sekä getter- että setter-funktiot sekä sen ominaisuuksille että myös funktion muodollisille parametreille.

    +

    Tästä seuraa, että muodollisen parametrin arvon muuttaminen muuttaa myös arguments-olion vastaavan ominaisuuden arvoa ja toisin päin.

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

    Suorituskykyyn liittyviä myyttejä ja totuuksia

    +

    arguments-olio luodaan aina paitsi jos se on jo julistettu nimenä funktiossa tai sen muodollisena parametrina. Tämä siitä huolimatta käytetäänkö sitä vai ei.

    +

    Sekä getter- ja setter-funktiot luodaan aina. Tästä seuraa, että niiden käytöllä ei ole juurikaan merkitystä suorituskyvyn kannalta.

    + +

    On kuitenkin eräs tapaus, jossa suorituskyky kärsii. Tämä liittyy arguments.callee-ominaisuuden käyttöön.

    +
    function foo() {
    +    arguments.callee; // tee jotain tällä funktio-oliolla
    +    arguments.callee.caller; // ja kutsuvalla funktio-oliolla
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // normaalisti tämä olisi inline-optimoitu
    +    }
    +}
    +

    Yllä olevassa koodissa foo-kutsua ei voida käsitellä avoimesti, koska sen tulee tietää sekä itsestään että kutsujasta. Sen lisäksi, että se haittaa suorituskykyä, rikkoo se myös kapseloinnin. Tässä tapauksessa funktio voi olla riippuvainen tietystä kutsuympäristöstä.

    +

    On erittäin suositeltavaa ettei arguments.callee-ominaisuutta tai sen ominaisuuksia käytetä ikinä.

    + +

    Konstruktorit

    JavaScriptin konstruktorit eroavat monista muista kielistä selvästi. Jokainen funktiokutsu, joka sisältää avainsanan new toimii konstruktorina.

    +

    Konstruktorin - kutsutun funktion - this-muuttujan arvo viittaa luotuun Object-olioon. Tämän uuden olion prototyyppi asetetaan osoittamaan konstruktorin kutsuman funktio-olion prototyyppiin.

    +

    Mikäli kutsuttu funktio ei sisällä selvää return-lausetta, tällöin se palauttaa this-muuttujan arvon eli uuden olion.

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

    Yllä Foo:ta kutsutaan konstruktorina. Juuri luodun olion prototyyppi asetetaan osoittamaan ominaisuuteen Foo.prototype.

    +

    Selvän return-lausekkeen tapauksessa funktio palauttaa ainoastaan määritellyn lausekkeen arvon. Tämä pätee tosin vain jos palautettava arvo on tyypiltään Object.

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // uusi olio
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // palautettu olio
    +

    Mikäli new-avainsanaa ei käytetä, funktio ei palauta uutta oliota.

    +
    function Foo() {
    +    this.bla = 1; // asetetaan globaalisti
    +}
    +Foo(); // undefined
    +

    Vaikka yllä oleva esimerkki saattaa näyttää toimivan joissain tapauksissa, viittaa this globaalin olion this-ominaisuuteen.

    +

    Tehtaat

    +

    Mikäli new-avainsanan käyttöä halutaan välttää, voidaan konstruktori pakottaa palauttamaan arvo.

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

    Tässä tapauksessa molemmat Bar-funktion kutsut käyttäytyvät samoin. Kumpikin kutsu palauttaa olion, joka sisältää method-ominaisuuden. Kyseinen ominaisuus on sulkeuma.

    +

    On myös tärkeää huomata, että kutsu new Bar() ei vaikuta palautetun olion prototyyppiin. Vaikka luodun olion prototyyppi onkin asetettu, Bar ei palauta ikinä kyseistä prototyyppioliota.

    +

    Yllä olevassa esimerkissä new-avainsanan käytöllä tai käyttämällä jättämisellä ei ole toiminnan kannalta mitään merkitystä.

    +

    Tehtaiden käyttö uusien olioiden luomiseen

    +

    Usein suositellaan new-avainsanan käytön välttämistä. Tämä johtuu siitä, että sen käyttämättä jättäminen voi johtaa bugeihin.

    +

    Sen sijaan suositellaan käytettävän tehdasta, jonka sisällä varsinainen olio konstruoidaan.

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

    Vaikka yllä oleva esimerkki välttää new-avainsanan käyttöä ja tekee paikallisten muuttujien käytön helpommaksi, sisältää se joitain huonoja puolia.

    +
      +
    1. Se käyttää enemmän muistia. Tämä johtuu siitä, että luodut oliot eivät jaa prototyypin metodeja.
    2. +
    3. Perinnän tapauksessa tehtaan tulee kopioida toisen olion kaikki metodit tai vaihtoehtoisesti asettaa kyseinen olio toisen prototyypiksi.
    4. +
    5. Prototyyppiketjun käsitteen unohtaminen on vain välttääksemme new-avainsanan käyttöä on vastoin kielen filosofista perustaa.
    6. +
    +

    Yhteenveto

    +

    Vaikka new-avainsanan käyttö voi johtaa bugeihin, prototyyppien käyttöä ei kannata unohtaa kokonaan. Loppujen lopuksi kyse on siitä, kumpi tapa sopii sovelluksen tarpeisiin paremmin. On erityisen tärkeää valita jokin tietty tapa ja pitäytyä sen käytössä.

    +

    Näkyvyysalueet ja nimiavaruudet

    Vaikka JavaScript-käyttääkin aaltosulkeita blokkien ilmaisuun, se ei tue blokkinäkyvyyttä. Tämä tarkoittaa sitä, että kieli tukee ainoastaan *funktionäkyvyyttä.

    +
    function test() { // näkyvyysalue
    +    for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    + +

    JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena globaalissa nimiavaruudessa.

    +

    Joka kerta kun muuttujaan viitataan, JavaScript käy kaikki näkyvyysalueet läpi alhaalta lähtien. Mikäli se saavuttaa globaalin näkyvyystalueen, eikä löydä haettua nimeä, se palauttaa ReferenceError-virheen.

    +

    Riesa nimeltä globaalit muuttujat

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

    Yllä olevat skriptit käyttäytyvät eri tavoin. Skripti A määrittelee muuttujan nimeltä foo globaalissa näkyvyysalueessa. Skripti B määrittelee foo-muuttujan vallitsevassa näkyvyysalueessa.

    +

    Tämä ei ole sama asia. var-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin.

    +
    // globaali näkyvyysalue
    +var foo = 42;
    +function test() {
    +    // paikallinen näkyvyysalue
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    var-avainsanan pois jättäminen johtaa siihen, että funktio test ylikirjoittaa foo:n arvon. Vaikka tämä ei välttämättä vaikutakaan suurelta asialta, tuhansien rivien tapauksessa var-avainsanan käyttämättömyys voi johtaa vaikeasti löydettäviin bugeihin.

    +
    // globaali näkyvyysalue
    +var items = [/* joku lista */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // aliluupin näkyvyysalue
    +    for(i = 0; i < 10; i++) { // hups, var jäi pois
    +        // jotain makeaa ja hienoa
    +    }
    +}
    +

    Tässä tapauksessa ulomman luupin suoritus lopetetaan ensimmäisen subLoop-kutsun jälkeen. Tämä johtuu siitä, että se ylikirjoittaa i:n globaalin arvon. Mikäli jälkimmäisessä luupissa olisi käytetty var-avainsanaa, olisi ikävyyksiltä vältytty. var-avainsanaa ei siis tule ikinä jättää pois ellei siihen ole hyvää syytä.

    +

    Paikalliset muuttujat

    +

    Ainoastaan funktion parametrit ja muuttujat, jotka sisältävät var-määreen ovat paikallisia.

    +
    // globaali näkyvyysalue
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // paikallinen näkyvyysalue
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    foo ja i ovatkin test-funktiolle paikallisia. bar sijoitus muuttaa globaalin muuttujan arvoa.

    +

    Hilaaminen

    +

    JavaScript hilaa määreitä. Tämä tarkoittaa sitä, että sekä var-lausekkeet että function-määreet siirretään ne sisältävän näkyvyysalueen huipulle.

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

    Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää var-lausekkeet ja function-määreet lähimmän näkyvyysalueen huipulle.

    +
    // var-lausekkeet siirrettiin tänne
    +var bar, someValue; // oletuksena 'undefined'
    +
    +// myös funktio-määre siirtyi tänne
    +function test(data) {
    +    var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // TypeError-virhe, baria ei ole vielä määritelty
    +someValue = 42; // hilaus ei koske sijoituksia
    +bar = function() {};
    +
    +test();
    +

    Sen lisäksi, että puuttuva blokkinäkyvyys siirtää var-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä if-rakenteista vaikeita käsittää.

    +

    Alkuperäisessä koodissa if-lause näytti muokkaavan globaalia muuttujaa goo. Todellisuudessa se muokkaa paikallista muuttujaa varsinaisen hilauksen jälkeen.

    +

    Seuraava koodi saattaisi ensi näkemältä aiheuttaa ReferenceError-virheen. Näin ei kuitenkaan tapahdu hilauksen ansiosta.

    +
    // onko SomeImportantThing alustettu
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Tämä toimii, koska var-lauseke on hilattu globaalin näkyvyysalueen huipulle.

    +
    var SomeImportantThing;
    +
    +// mahdollista alustuskoodia
    +
    +// onhan se alustettu
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Nimienerottelujärjestys

    +

    Kaikki JavaScriptin näkyvyysalueet, globaalin näkyvyysalue mukaanlukien, sisältävät erikoismuuttujan this. this viittaa tämänhetkiseen olioon.

    +

    Funktioiden näkyvyysalueet sisältävät myös arguments-olion. Se sisältää funktiolle annetut argumentit.

    +

    Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi foo:n arvoon JavaScript käyttäytyy seuraavasti:

    +
      +
    1. Mikäli var foo-lauseke löytyy tämänhetkisestä näkyvyysalueesta, käytä sen arvoa.
    2. +
    3. Mikäli eräs funktion parametreista on foo, käytä sitä.
    4. +
    5. Mikäli funktion nimi itsessään on foo, käytä sitä.
    6. +
    7. Siirry ulompaan näkyvyysalueeseen ja suorita #1 uudelleen.
    8. +
    + +

    Nimiavaruudet

    +

    Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä nimettömiä kääreitä.

    +
    (function() {
    +    // "nimiavaruus" itsessään
    +
    +    window.foo = function() {
    +        // paljastettu sulkeuma
    +    };
    +
    +})(); // suorita funktio heti
    +

    Nimettömiä funktioita pidetään lauseina. Jotta niitä voidaan kutsua, tulee ne suorittaa ensin.

    +
    ( // suorita sulkeiden sisältämä funktio
    +function() {}
    +) // ja palauta funktio-olio
    +() // kutsu suorituksen tulosta
    +

    Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen.

    +
    // Kaksi muuta tapaa
    ++function(){}();
    +(function(){}());
    +

    Yhteenveto

    +

    On suositeltavaa käyttää nimettömiä kääreitä nimiavaruuksina. Sen lisäksi, että se suojelee koodia nimitörmäyksiltä, se tarjoaa keinon jaotella ohjelma paremmin.

    +

    Globaalien muuttujien käyttöä pidetään yleisesti huonona tapana. Mikä tahansa niiden käyttö viittaa huonosti kirjoitettuun, virheille alttiiseen ja hankalasti ylläpidettävään koodiin.

    +

    Taulukot

    Taulukon iterointi ja attribuutit

    Vaikka taulukot ovatkin JavaScript-olioita, niiden tapauksessa ei välttämättä kannata käyttää for in loop-luuppia. Pikemminkin tätä tapaa tulee välttää.

    + +

    for in-luuppi iteroi kaikki prototyyppiketjun sisältämät ominaisuudet. Tämän vuoksi tulee käyttää erityistä hasOwnProperty-metodia, jonka avulla voidaan taata, että käsitellään oikeita ominaisuuksia. Tästä johtuen iteroint on jo lähtökohtaisesti jopa kaksikymmentä kertaa hitaampaa kuin normaalin for-luupin tapauksessa.

    +

    Iterointi

    +

    Taulukkojen tapauksessa paras suorituskyky voidaan saavuttaa käyttämällä klassista for-luuppia.

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

    Edelliseen esimerkkiin liittyy yksi mutta. Listan pituus on tallennettu välimuistiin erikseen käyttämällä l = list.length-lauseketta.

    +

    Vaikka length-ominaisuus määritelläänkin taulukossa itsessään, arvon hakeminen sisältää ylimääräisen operaation. Uudehkot JavaScript-ympäristöt saattavat optimoida tämän tapauksen. Tästä ei kuitenkaan ole mitään takeita.

    +

    Todellisuudessa välimuistin käytön pois jättäminen voi hidastaa luuppia jopa puolella.

    +

    length-ominaisuus

    +

    length-ominaisuuden getteri palauttaa yksinkertaisesti taulukon sisältämien alkioiden määrän. Sen setteriä voidaan käyttää taulukon typistämiseen.

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

    Pituuden pienemmäksi asettaminen typistää taulukkoa. Sen kasvattaminen ei kuitenkaan vaikuta mitenkään.

    +

    Yhteenveto

    +

    Parhaan suorituskyvyn kannalta on parhainta käyttää tavallista for-luuppia ja tallentaa length-ominaisuus välimuistiin. for in-luupin käyttö taulukon tapauksessa on merkki huonosti kirjoitetusta koodista, joka on altis bugeille ja heikolle suorituskyvylle.

    +

    Array-konstruktori

    Array-oletuskonstruktorin käytös ei ole lainkaan yksiselitteistä. Tämän vuoksi suositellaankin, että konstruktorin sijasta käytetään literaalinotaatiota [].

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

    Mikäli Array-konstruktorille annetaan vain yksi argumentti ja se on tyypiltään Number, konstruktori palauttaa uuden harvan taulukon, jonka length-attribuutti on asetettu annetun numeron mukaisesti. On tärkeää huomata, että ainoastaan length asetetaan tällä tavoin, todellisia taulukon indeksejä ei alusteta.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, indeksiä ei ole alustettu
    +

    Tämä on käytännöllistä vain harvoin, kuten merkkijonon toiston tapauksessa. Tällöin voidaan välttää for-luupin käyttämistä.

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

    Yhteenveto

    +

    Array-konstruktorin käyttöä tulee käyttää niin paljon kuin suinkin mahdollista. Sen sijaan on suositeltavaa käyttää literaalinotaatiota. Literaalit ovat lyhyempiä ja niiden syntaksi on selkeämpi. Tämän lisäksi ne tekevät koodista luettavampaa.

    +

    Tyypit

    Yhtäsuuruus ja vertailut

    JavaScript sisältää kaksi erilaista tapaa, joiden avulla olioiden arvoa voidaan verrata toisiinsa.

    +

    Yhtäsuuruusoperaattori

    +

    Yhtäsuuruusoperaattori koostuu kahdesta yhtäsuuruusmerkistä: ==

    +

    JavaScript tyypittyy heikosti. Tämä tarkoittaa sitä, että yhtäsuuruusoperaattori muuttaa tyyppejä verratakseen niitä keskenään.

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

    Yllä oleva taulukko näyttää tyyppimuunnoksen tulokset. Tämä onkin eräs pääsyistä, minkä vuoksi ==-operaattorin käyttöä pidetään huonona asiana. Sen käyttö johtaa hankalasti löydettäviin bugeihin monimutkaisista muunnossäännöistä johtuen.

    +

    Tämän lisäksi tyyppimuunnos vaikuttaa suorituskykyyn. Esimerkiksi merkkijono tulee muuttaa numeroksi ennen kuin sitä voidaan verrata toiseen numeroon.

    +

    Tiukka yhtäsuuruusoperaattori

    +

    Tiukka yhtäsuuruusoperaattori koostuu kolmesta yhtäsuuruusmerkistä: ===

    +

    Se toimii aivan kuten normaali yhtäsuuruusoperaattori. Se ei tosin tee minkäänlaista tyyppimuunnosta ennen vertailua.

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

    Yllä olevat tulokset ovat huomattavasti selkeämpiä ja mahdollistavat koodin menemisen rikki ajoissa. Tämä kovettaa koodia ja tarjoaa myös parempaa suorituskykyä siinä tapauksessa, että operandit ovat erityyppisiä.

    +

    Olioiden vertailu

    +

    Vaikka sekä == ja === ovat yhtäsuuruusoperaattoreita, ne toimivat eri tavoin, kun ainakin yksi operandeista sattuu olemaan Object.

    +
    {} === {};                   // epätosi
    +new String('foo') === 'foo'; // epätosi
    +new Number(10) === 10;       // epätosi
    +var foo = {};
    +foo === foo;                 // tosi
    +

    Tässä tapauksessa molemmat operaattorit vertaavat olion identiteettiä eikä sen arvoa. Tämä tarkoittaa sitä, että vertailu tehdään olion instanssin tasolla aivan, kuten Pythonin is-operaattorin tai C:n osoitinvertailun tapauksessa.

    +

    Yhteenveto

    +

    On erittäin suositeltavaa, että ainoastaan tiukkaa yhtäsuuruusoperaattoria käytetään. Mikäli tyyppejä tulee muuttaa, tämä kannattaa tehdä selvästi sen sijaan että luottaisi kielen monimutkaisiin muunnossääntöihin.

    +

    typeof-operaattori

    typeof-operaattori, kuten myös instanceof, on kenties JavaScriptin suurin suunnitteluvirhe. Tämä johtuu siitä, että nämä ominaisuudet ovat liki kokonaan käyttökelvottomia.

    +

    Vaikka instanceof-operaattorilla onkin tiettyjä rajattuja käyttötarkoituksia, typeof-operaattorille on olemassa vain yksi käytännöllinen käyttötapaus, joka ei tapahdu olion tyyppiä tarkasteltaessa.

    + +

    JavaScriptin tyyppitaulukko

    +
    Arvo                Luokka     Tyyppi
    +-------------------------------------
    +"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 (Nitro/V8-funktio)
    +new RegExp("meow")  RegExp     object (Nitro/V8-funktio)
    +{}                  Object     object
    +new Object()        Object     object
    +

    Yllä olevassa taulukossa Tyyppi viittaa arvoon, jonka typeof-operaattori palauttaa. Kuten voidaan havaita, tämä arvo voi olla varsin ristiriitainen.

    +

    Luokka viittaa olion sisäisen [[Luokka]]-ominaisuuden arvoon.

    + +

    Jotta kyseiseen arvoon päästään käsiksi, tulee soveltaa Object.prototype-ominaisuuden toString-metodia.

    +

    Olion luokka

    +

    Määritelmä antaa tarkalleen yhden keinon, jonka avulla [[Luokka]] arvoon voidaan päästä käsiksi. Tämä on mahdollista Object.prototype.toString-metodia käyttäen.

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

    Yllä olevassa esimerkissä Object.prototype.toString-metodia kutsutaan arvolla this, jonka arvo on asetettu olion [[Luokka]] arvoon.

    + +

    Määrittelemättömien muuttujien testaaminen

    +
    typeof foo !== 'undefined'
    +

    Yllä oleva testi kertoo onko foo määritelty. Pelkästään siihen viittaaminen palauttaisi ReferenceError-virheen. Tämä on ainut asia, johon typeof-operaattoria kannattaa käyttää.

    +

    Yhteenveto

    +

    Ainut tapa, jonka avulla olion tyyppi voidaan tarkistaa luotettavasti, on Object.prototype.toString-metodin käyttö, kuten yllä. Kuten yllä oleva tyyppitaulu näyttää, osa typeof-operaattorin palautusarvoista on huonosti määritelty. Tästä johtuen ne voivat erota toteutuksesta riippuen.

    +

    Muuttujan määrittelemättömyyden testaaminen on ainut tapaus, jossa typeof-operaattoria kannattaa käyttää. Muutoin sen käyttöä kannattaa välttää hinnalla milla hyvänsä.

    +

    instanceof-operaattori

    instanceof-operaattori vertaa kahden operandinsa konstruktoreita keskenään. Se on hyödyllinen ainoastaan, kun vertaillaan itsetehtyjä olioita. Natiivien tyyppien tapauksessa se on lähes yhtä hyödytön kuin typeof-operaattori.

    +

    Itsetehtyjen olioiden vertailu

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // tosi
    +new Bar() instanceof Foo; // tosi
    +
    +// Tämä asettaa vain Bar.prototype-ominaisuudeksi
    +// funktio-olion Foo
    +// Se ei kuitenkaan ole Foon todellinen instanssi
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // epätosi
    +

    instanceof ja natiivit tyypit

    +
    new String('foo') instanceof String; // tosi
    +new String('foo') instanceof Object; // tosi
    +
    +'foo' instanceof String; // epätosi
    +'foo' instanceof Object; // epätosi
    +

    On tärkeää huomata, että instanceof ei toimi olioilla, jotka tulevat muista JavaScript-konteksteista (esim. selaimen eri dokumenteista). Tässä tapauksessa niiden konstruktorit viittaavat eri olioon.

    +

    Yhteenveto

    +

    instanceof-operaattoria tulee käyttää ainoastaan, mikäli käsitellään itsetehtyjä olioita saman JavaScript-kontekstin sisällä. Kuten typeof-operaattorikin, myös muita sen käyttöjä tulee välttää.

    +

    Tyyppimuunnokset

    JavaScript on tyypitetty heikosti. Tämä tarkoittaa sitä, että se pyrkii pakottamaan tyyppejä aina kun se on mahdollista.

    +
    // Nämä ovat totta
    +new Number(10) == 10; // Number.toString() muutetaan
    +                      // takaisin numeroksi
    +
    +10 == '10';           // Merkkijonot muutetaan Number-tyyppiin
    +10 == '+10 ';         // Lisää merkkijonohauskuutta
    +10 == '010';          // Ja lisää
    +isNaN(null) == false; // null muuttuu nollaksi,
    +                      // joka ei ole NaN
    +
    +// Nämä ovat epätosia
    +10 == 010;
    +10 == '-10';
    + +

    Yllä havaittu käytös voidaan välttää käyttämällä tiukkaa vertailuoperaattoria. Sen käyttöä suositellaan lämpimästi. Vaikka se välttääkin useita yleisiä ongelma, sisältää se omat ongelmansa, jotka johtavat juurensa JavaScriptin heikkoon tyypitykseen.

    +

    Natiivien tyyppien konstruktorit

    +

    Natiivien tyyppien, kuten Number tai String, konstruktorit käyttäytyvät eri tavoin new-avainsanan kanssa ja ilman.

    +
    new Number(10) === 10;     // Epätosi, Object ja Number
    +Number(10) === 10;         // Tosi, Number ja Number
    +new Number(10) + 0 === 10; // Tosi, johtuu tyyppimuunnoksesta
    +

    Number-tyypin kaltaisen natiivityypin käyttäminen luo uuden Number-olion. new-avainsanan pois jättäminen tekee Number-funktiosta pikemminkin muuntimen.

    +

    Tämän lisäksi literaalit tai ei-oliomaiset arvot johtavat edelleen uusiin tyyppimuunnoksiin.

    +

    Paras tapa suorittaa tyyppimuunnoksia on tehdä niitä selvästi.

    +

    Muunnos merkkijonoksi

    +
    '' + 10 === '10'; // tosi
    +

    Arvo voidaan muuttaa merkkijonoksi helposti lisäämällä sen eteen tyhjä merkkijono.

    +

    Muunnos numeroksi

    +
    +'10' === 10; // tosi
    +

    Unaarinen plus-operaattori mahdollistaa numeroksi muuttamisen.

    +

    Muunnos totuusarvoksi

    +

    Arvo voidaan muuttaa totuusarvoksi käyttämällä not-operaattoria kahdesti.

    +
    !!'foo';   // tosi
    +!!'';      // epätosi
    +!!'0';     // tosi
    +!!'1';     // tosi
    +!!'-1'     // tosi
    +!!{};      // tosi
    +!!true;    // tosi
    +

    Ydin

    Miksi eval-funktiota tulee välttää

    eval suorittaa JavaScript-koodia sisältävän merkkijonon paikallisessa näkyvyysalueessa.

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

    eval suoritetaan paikallisessa näkyvyysalueessa ainoastaan kun sitä kutsutaan suorasti ja kutsutun funktion nimi on todellisuudessa eval.

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

    eval-funktion käyttöä tulee välttää ehdottomasti. 99.9% sen "käyttötapauksista" voidaan toteuttaa ilman sitä.

    +

    Piilotettu eval

    +

    Aikakatkaisufunktiot setTimeout and setInterval voivat kumpikin ottaa merkkijonon ensimmäisenä argumenttinaan. Kyseinen merkkijono suoritetaan aina globaalissa näkyvyysalueessa, koska tuolloin eval-funktiota kutsutaan epäsuorasti.

    +

    Turvallisuusongelmat

    +

    eval on myös turvallisuusongelma. Se suorittaa minkä tahansa sille annetun koodin. Tämän vuoksi sitä ei tule ikinä käyttää tuntemattomasta tai epäluotttavasta lähteestä tulevien merkkijonojen kanssa.

    +

    Yhteenveto

    +

    eval-funktiota ei pitäisi käyttää koskaan. Mikä tahansa sitä käyttävä koodi on kyseenalaista sekä suorituskyvyn että turvallisuuden suhteen. Mikäli jokin tarvitsee eval-funktiota toimiakseen, tulee sen suunnittelutapa kyseenalaistaa. Tässä tapauksessa on parempi suunnitella toisin ja välttää eval-funktion käyttöä.

    +

    undefined ja null

    JavaScript sisältää kaksi erillistä arvoa ei millekään. Näistä hyödyllisempti on undefined.

    +

    undefined ja sen arvo

    +

    undefined on tyyppi, jolla on vain yksi arvo: undefined.

    +

    Kieli määrittelee myös globaalin muuttujan, jonka arvo on undefined. Myös tätä arvoa kutsutaan nimellä undefined. Tämä muuttuja ei kuitenkaan ole vakio eikä kielen avainsana. Tämä tarkoittaa siis sitä, että sen arvo voidaan ylikirjoittaa.

    + +

    Seuraavat tapaukset palauttavat undefined-arvon:

    +
      +
    • Globaalin (muokkaamattoman) muuttujan undefined arvon haku.
    • +
    • Puuttuvista return-lauseista seuraavat epäsuorat palautusarvot.
    • +
    • return-lauseet, jotka eivät palauta selvästi mitään.
    • +
    • Olemattomien ominaisuuksien haut.
    • +
    • Funktioparametrit, joiden arvoa ei ole asetettu.
    • +
    • Mikä tahansa, joka on asetettu arvoon undefined.
    • +
    +

    Arvon undefined muutosten hallinta

    +

    Koska globaali muuttuja undefined sisältää ainoastaan todellisen undefined-tyypin arvon kopion, ei sen asettamienn uudelleen muuta tyypin undefined arvoa.

    +

    Kuitenkin, jotta undefined-tyypin arvoa voidaan verrata, tulee sen arvo voida hakea jotenkin ensin.

    +

    Tätä varten käytetään yleisesti seuraavaa tekniikkaa. Ajatuksena on antaa itse arvo käyttäen nimetöntä käärettä.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // paikallisen näkyvyysalueen undefined 
    +    // voi viitata jälleen todelliseen arvoon
    +
    +})('Hello World', 42);
    +

    Samaan lopputuloksen voidaan päästä myös käyttämällä esittelyä kääreen sisällä.

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

    Tässä tapauksessa ainut ero on se, että pakattu versio vie 4 tavua enemmän tilaa 'var'-lauseen vuoksi.

    +

    null ja sen käyttötapaukset

    +

    Vaikka undefined-arvoa käytetäänkin usein perinteisen null-arvon sijasta, todellinen null (sekä literaali että tyyppi) on enemmän tai vähemmän vain tietotyyppi.

    +

    Sitä käytetään joissain JavaScriptin sisäisissä toiminnoissa, kuten prototyyppiketjun pään toteamisessa (Foo.prototype = null). Useimmissa tapauksissa se voidaan korvata undefined-arvoa käyttäen.

    +

    Automaattiset puolipisteet

    Vaikka JavaScript käyttääkin C:n tapaista syntaksia, se ei pakota käyttämään puolipisteitä. Niiden käyttöä voidaan halutessa välttää.

    +

    Tästä huolimatta JavaScript ei kuitenkaan ole puolipisteetön kieli. Se tarvitsee niitä ymmärtääkseen lähdekoodia. Tämän vuoksi JavaScript-parseri lisää niitä tarpeen mukaan automaattisesti.

    +
    var foo = function() {
    +} // parsimisvirhe, lisätään puolipiste
    +test()
    +

    Lisäys tapahtuu ja parseri yrittää uudelleen.

    +
    var foo = function() {
    +}; // ei virhettä, parsiminen jatkuu
    +test()
    +

    Automaattista puolipisteiden lisäämistä pidetään eräänä JavaScriptin suurimmista suunnitteluvirheistä. Tämä johtuu siitä, että se voi muuttaa tapaa, jolla koodi käyttäytyy.

    +

    Kuinka se toimii

    +

    Alla oleva koodi ei sisällä puolipisteitä. Täten niiden lisääminen jää parserin tehtäväksi.

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

    Alla parserin arvaus.

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Not inserted, lines got merged
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- lisätty
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- lisätty
    +
    +        return; // <- lisätty, rikkoo return-lauseen
    +        { // kohdellaan lohkona
    +
    +            // nimike ja yhden lausekkeen lause
    +            foo: function() {} 
    +        }; // <- lisätty
    +    }
    +    window.test = test; // <- lisätty
    +
    +// Rivit yhdistettiin jälleen
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- lisätty
    +
    +})(window); //<- lisätty
    + +

    Yllä olevassa tapauksessa parseri muutti huomattavasti koodin käytöstä. Joissain tapauksissa se tekee kokonaan väärän asian.

    +

    Johtavat sulkeet

    +

    Parseri ei lisää puolipistettä johtavien sulkeiden tapauksessa.

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

    Koodi muuttuu seuraavaksi.

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

    On hyvin mahdollista, että log ei palauta funktiota. Tästä johtuen yllä oleva palauttanee TypeError-virheen, joka toteaa että undefined ei ole funktio.

    +

    Yhteenveto

    +

    On suositeltavaa ettei puolipisteitä jätetä pois milloinkaan. Tämän lisäksi sulut kannattaa pitää niitä vastaavien lausekkeiden kanssa samalla rivillään. if ja else-lauseiden tapauksessa sulkuja kannattaa käyttää aina. Sen lisäksi että edellä mainitut suositukset tekevät koodista johdonmukaisempaa, estävät ne myös JavaScript-parseria muuttamasta sen käytöstapaa.

    +

    Muuta

    setTimeout ja setInterval

    Koska JavaScript on luonteeltaan asynkroninen, voidaan funktioiden suoritusta ajastaa käyttäen setTimeout sekä setInterval-funktioita.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // palauttaa Numeron > 0
    +

    Kun setTimeout-funktiota kutsutaan, se palauttaa aikakatkaisun tunnisteen ja ajastaa foo-funktion suoritettavaksi suunnilleen tuhannen millisekunnin päästä. foo suoritetaan tarkalleen kerran.

    +

    Käytössä olevan JavaScript-tulkin ajastimen tarkkuudesta, JavaScriptin yksisäikeisyydestä sekä muusta koodista riippuen ei ole lainkaan taattua, että viive on tarkalleen sama kuin määritelty.

    +

    Ensimmäisenä annettu funktio suoritetaan globaalisti. Tämä tarkoittaa sitä, että sen this on asetettu osoittamaan globaaliin olioon.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this viittaa globaaliin olioon
    +        console.log(this.value); // tulostaa undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Kutsujen pinoaminen setInterval-funktion avulla

    +

    setTimeout suoritetaan vain kerran. setInterval sen sijaan, kuten nimestä voi päätellä, suoritetaan aina X millisekunnin välein. Sen käyttöä ei kuitenkaan suositella.

    +

    Mikäli suoritettava koodi blokkaa katkaisufunktion kutsun, setInterval lisää kutsuja pinoon. Tämä voi olla ongelmallista erityisesti, mikäli käytetään pieniä intervalliarvoja.

    +
    function foo(){
    +    // jotain joka blokkaa sekunnin ajaksi
    +}
    +setInterval(foo, 100);
    +

    Yllä olevassa koodissa foo-funktiota kutsutaan, jonka jälleen se blokkaa sekunnin ajan.

    +

    Tämän ajan aikana setInterval kasvattaa kutsupinon sisältöä. Kun foo on valmis, kutsupinoon on ilmestynyt jo kymmenen uutta kutsua suoritettavaksi.

    +

    Mahdollisesti blokkaavan koodin kanssa pärjääminen

    +

    Helpoin ja joustavin tapa on käyttää setTimeout-funktiota funktiossa itsessään.

    +
    function foo(){
    +    // jotain joka blokkaa sekunnin ajaksi
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Sen lisäksi että tämä ratkaisu kapseloi setTimeout-kutsun, se myös estää kutsujen pinoutumisen ja tarjoaa joustavuutta. foo voi päättää halutaanko se suorittaa uudelleen vai ei.

    +

    Katkaisujen poistaminen käsin

    +

    Katkaisuja ja intervalleja voidaan poistaa antamalla sopiva tunniste joko clearTimeout- tai clearInterval-funktiolle. Se kumpaa käytetään riippuu käytetystä set-funktiosta.

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

    Kaikkien katkaisujen poistaminen

    +

    JavaScript ei sisällä erityistä funktiota kaikkien katkaisujen ja/tai intervallien poistamiseen. Sen sijaan tämä voidaan toteuttaa raakaa voimaa käyttäen.

    +
    // poista "kaikki" katkaisut
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    On mahdollista, että jopa tämän jälkeen on olemassa katkaisuja, jotka ovat käynnissä. Onkin siis suositeltavaa tallentaa katkaisujen tunnisteet jotenkin. Tällä tavoin ne voidaan poistaa käsin.

    +

    Piilotettu eval

    +

    setTimeout ja setInterval voivat ottaa myös merkkijonon ensimmäisenä parametrinaan. Tätä ominaisuutta ei tule käyttää ikinä, koska se käyttää sisäisesti eval-funktiota.

    + +
    function foo() {
    +    // kutsutaan
    +}
    +
    +function bar() {
    +    function foo() {
    +        // ei kutsuta ikinä
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Koska eval-funktiota ei kutsuta suoraan, setTimeout-funktiolle annettu merkkijono suoritetaan globaalissa näkyvyysalueessa. Tässä tapauksessa se ei siis käytä paikallista bar-funktion näkyvyysalueessa olevaa foo-funktiota.

    +

    Tämän lisäksi on suositeltavaa olla käyttämättä merkkijonoja parametrien antamiseen.

    +
    function foo(a, b, c) {}
    +
    +// Älä käytä tätä IKINÄ
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// Käytä nimetöntä funktiota sen sijaan
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    Yhteenveto

    +

    Merkkijonoa ei tule antaa setTimeout- tai setInterval-funktiolle koskaan. Tämä on selvä merkki erittäin huonosta koodista erityisesti mikäli sitä käytetään parametrien välittämiseen. Sen sijaan kannattaa käyttää nimetöntä funktiota, joka huolehtii varsinaisesta kutsusta.

    +

    Tämän lisäksi setInterval-funktion käyttöä tulee välttää. Tämä johtuu siitä, että sen JavaScript ei blokkaa sen vuorottajaa.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/fr/index.html b/external/JavaScript-Garden/fr/index.html new file mode 100644 index 0000000..13f8a21 --- /dev/null +++ b/external/JavaScript-Garden/fr/index.html @@ -0,0 +1,1197 @@ +Le Jardin de JavaScript

    Introduction

    Introduction

    Le Jardin de JavaScript est une collection croissante de documentation liée aux aspects les plus excentriques du langage de programmation JavaScript. Il donne des conseils pour éviter les erreurs communes, les bugs subtils, ainsi que les problèmes de performance et de mauvaises pratiques, que les amateurs de JavaScript peuvent rencontrer dans leurs efforts d'apprentissage en profondeur du langage.

    +

    Le Jardin de JavaScript ne cherche pas à vous enseigner JavaScript. Une connaissance préalable du langage est fortement recommandée afin de comprendre les sujets abordés dans ce guide. Veuillez vous référer à l'excellent guide du Mozilla Developer Network pour apprendre les rudiments du langage JavaScript.

    +

    Auteurs

    +

    Ce guide est l'œuvre de deux charmants utilisateurs de Stack Overflow: Ivo Wetzel (écriture) et Zhang Yi Jiang (design).

    +

    Actuellement maintenu par Tim Ruffles.

    +

    Collaborateurs

    + +

    Hébergement

    +

    Le Jardin de JavaScript est hébergé sur GitHub, mais Cramer Développement nous soutient avec un mirroir à JavaScriptGarden.info.

    +

    Licence

    +

    Le Jardin de JavaScript est publié sous la licence MIT et hébergé sur GitHub. Si vous trouvez des erreurs ou fautes de frappe veuillez s'il vous plaît déposer une question ou une "pull request" sur le dépôt. Vous pouvez également nous trouver dans la Salle JavaScript sur Stack Overflow.

    +

    Objets

    Utilisation des objets et propriétés

    En JavaScript, tout agit comme un objet, à part deux exceptions: null et undefined.

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

    Une méprise commune est que les littéraux numériques ne peuvent pas être utilisés comme objets, due à une imperfection de l'analyseur de JavaScript qui tente d'analyser la notation à point sur un nombre comme une virgule flottante.

    +
    2.toString(); // erreur de syntaxe SyntaxError
    +

    Des solutions de contournement existent pour forcer les littéraux numériques à agir comme des objets.

    +
    2..toString(); // le second point est correctement reconnu
    +2 .toString(); // notez l'espace à gauche du point
    +(2).toString(); // 2 est évalué en premier
    +

    Objets comme type de données

    +

    Les objets en JavaScript peuvent également être utilisés comme HashMaps; essentiellement, des propriétés nommées pointant sur des valeurs.

    +

    En utilisant un littéral d'objet - notation {} - il est possible de créer un objet vide. +Ce nouvel objet hérite de Object.prototype et ne possède pas de propriétés propres définies.

    +
    var foo = {}; // un nouvel objet vide
    +
    +// un nouvel objet avec une propriété 'test' à valeur 12
    +var bar = {test: 12};
    +

    Accéder aux propriétés

    +

    Les propriétés d'un objet sont accessibles de deux façons, soit par la notation à point, soit par la notation à crochets.

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

    Les deux notations fonctionnent presque pareil, la seule différence étant que la notation à crochet permet l'écriture des propriétés et l'utilisation des noms de propriété qui autrement mèneraient à une erreur de syntaxe.

    +

    Supprimer des propriétés

    +

    La seule façon de supprimer une propriété d'un objet est d'utiliser l'opérateur delete. +Mettre la propriété à null ou undefined ne supprime que la valeur associée à la propriété, et non pas la propriété elle-même.

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

    Les résultats du programme ci-dessus sont bar undefined et foo null - seul baz a été correctement supprimé.

    +

    Notation des clefs "keys"

    +
    var test = {
    +    'case': 'Je suis un mot-clé, donc je dois etre écrit en tant que chaîne',
    +    delete: 'Je suis un mot-clé, donc moi aussi' // erreur de syntaxe SyntaxError
    +};
    +

    Les propriétés d'objet peuvent être écrites simplement telles quelles ou comme des chaînes "string". Une autre imperfection de l'analyseur de JavaScript, avant ECMAScript 5, provoquera une erreur de syntaxe SyntaxError dans le programme qui précède.

    +

    Cette erreur vient du fait que delete est un mot-clé; et par conséquent, il doit être écrit comme une chaîne littérale pour s'assurer qu'il sera correctement interprété par les vieux moteurs JavaScript.

    +

    Le prototype

    JavaScript n'utilise pas le modèle classique d'héritage, mais un modèle prototypique.

    +

    Souvent considéré comme l'une des faiblesses de JavaScript, le modèle d'héritage prototypique est en fait plus puissant que le modèle classique. Par exemple, il est assez facile de construire un modèle classique à partir du modèle prototypique, tandis que l'inverse est une tâche beaucoup plus difficile à entreprendre.

    +

    JavaScript étant le seul langage à héritage prototypique largement utilisé, s'adapter aux différences entre les deux modèles peut prendre du temps.

    +

    La première différence majeure est que l'héritage en JavaScript utilise des chaînes de prototypes.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Assigner le prototype de Bar à une nouvelle instance de Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// S'assurer que Bar est le constructeur
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // crée une nouvelle instance de bar
    +
    +// La chaîne de prototypes qui en résulte
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    Dans le code ci-dessus, l'objet test va hériter à la fois de Bar.prototype et de Foo.prototype; par conséquent, il aura accès à la fonction method qui était définie sur Foo. Il aura également accès à la propriété value de la seule instance de Foo qui est son prototype. Il est important de noter que le new Bar() ne crée pas une nouvelle instance de Foo, mais réutilise celui attribué à son prototype; ainsi, toutes les instances de Bar se partageront la même propriété value.

    + +

    Recherche des propriétés

    +

    Lors de l'accès aux propriétés d'un objet, JavaScript traversera la chaîne de prototypes vers le haut jusqu'à ce qu'il trouve une propriété avec le nom demandé.

    +

    S'il atteint le sommet de la chaîne - à savoir Object.prototype - sans avoir trouvé la propriété spécifiée, la valeur undefined sera retournée.

    +

    La propriété prototype

    +

    Bien que la propriété prototype est utilisé par le langage pour construire la chaîne de prototypes, il est toujours possible de lui attribuer une valeur quelconque, mais les types primitifs seront simplement ignorés.

    +
    function Foo() {}
    +Foo.prototype = 1; // aucun effet
    +

    Assigner des objets, comme le montre l'exemple ci-dessus, va marcher, et permet la création dynamique de chaînes de prototypes.

    +

    Performance

    +

    Les temps de recherche pour des propriétés qui sont en haut de la chaîne de prototypes peuvent avoir un impact négatif qui être significatif pour du code où la performance est critique. Essayer d'accéder à des propriétés inexistantes causera toujours la traversée complète de la chaîne de prototypes.

    +

    De plus, itérer sur les propriétés d'un objet va causer l'énumération de toutes les propriétés qui se trouve sur la chaîne de prototype.

    +

    Extension des prototypes natifs

    +

    Une mauvaise technique souvent utilisée est d'étendre Object.prototype ou un des prototypes intégrés.

    +

    Cette technique est appelée monkey patching et casse l'encapsulation. Bien qu'utilisée par des cadriciels "frameworks" populaires tels que Prototype, il n'existe aucune bonne raison pour encombrer les types intégrés avec des fonctionnalités supplémentaires non standards.

    +

    La seule bonne raison d'étendre un prototype intégré est le rétroportage de caractéristiques des nouveaux moteurs JavaScript; par exemple, Array.forEach.

    +

    En conclusion

    +

    Il est essentiel de comprendre le modèle d'héritage prototypique avant d'écrire du code complexe qui l'utilise. Soyez conscient de la longueur des chaînes de prototypes dans votre code; découpez les si nécessaire pour éviter de possible problèmes de performance. En outre, les prototypes natifs ne devraient jamais être étendus, sauf pour des raisons de compatibilité avec de nouvelles caractéristiques du langage JavaScript.

    +

    hasOwnProperty

    Pour savoir si un objet possède une propriété définie, et non pas quelque part ailleurs sur sa chaîne de prototype, il est nécessaire d'utiliser la méthode hasOwnProperty, une méthode que tous les objets héritent d'Object.prototype.

    + +

    hasOwnProperty est la seule chose en JavaScript qui traite des propriétés sans traverser la chaîne de prototypes.

    +
    // Empoisonnement d'Object.prototype
    +Object.prototype.bar = 1;
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // vrai
    +
    +foo.hasOwnProperty('bar'); // faux
    +foo.hasOwnProperty('goo'); // vrai
    +

    Seulement hasOwnProperty donnera le résultat attendu et correct. Voir la section sur les boucles for in pour plus de détails sur l'utilisation de hasOwnProperty pour traverser les propriétés d'un objet.

    +

    hasOwnProperty en tant que propriété

    +

    JavaScript ne protège pas le nom de la propriété hasOwnProperty; ainsi, la possibilité existe qu'un objet peut avoir une propriété avec ce nom, et il est donc nécessaire d'utiliser une méthode hasOwnProperty externe pour obtenir des résultats corrects.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // toujours faux
    +
    +// Utiliser hasOwnProperty d'un autre object,
    +// et l'appeler avec foo assigné à 'this'
    +({}).hasOwnProperty.call(foo, 'bar'); // vrai
    +
    +// Il est aussi possible d'utiliser hasOwnProperty
    +//du prototype d'Object
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // vrai
    +

    En conclusion

    +

    Utiliser hasOwnProperty est la seule méthode fiable pour vérifier l'existence d'une propriété sur un objet. Il est recommandé d'utiliser hasOwnProperty pour itérer sur les propriétés des objets comme décrit dans la section sur les boucles for in.

    +

    La boucle for in

    Tout comme l'opérateur in, la boucle for in traverse la chaîne de prototypes lors de l'itération sur les propriétés d'un objet.

    + +
    // Empoisonnement d'Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // imprime bar et moo
    +}
    +

    Puisqu'il n'est pas possible de changer le comportement de la boucle for in, il est nécessaire de filtrer les propriétés indésirables à l'intérieur du corps de la boucle. Sous ECMAScript 3 et plus, cela se fait en utilisant la méthode hasOwnProperty de Object.prototype.

    +

    Depuis ECMAScript 5, Object.defineProperty peut être utilisé avec enumerable mis à faux pour ajouter des propriétés à des objets (y compris Object) sans que ces propriétés soient énumérées. Il est raisonnable dans ce cas d'assumer que les propriétés énumérables ont été ajouté pour une raison, ce qui permet d'omettre les appels à hasOwnProperty qui réduisent la lisibilité du code. Dans du code de librairie, hasOwnProperty devrait toujours être utilisé car des propriétés énumérables pourraient résider sur la chaîne de prototypes sans qu'on le sache.

    + +

    Filtrer avec hasOwnProperty

    +
    // le même foo qu'au dessus
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Cette version est la seule version correcte à utiliser avec les anciennes versions d'ECMAScript. +L'utilisation de hasOwnProperty nous garantie que seulement moo sera imprimé. +Quand hasOwnProperty n'est pas utilisé, les prototypes natifs - par exemple Object.prototype - qui ont peut-être été étendus, causeront probablement des erreurs.

    +

    Avec les versions plus récentes d'ECMAScript, des propriétés non-dénombrables peuvent être définies avec Object.defineProperty, réduisant le risque d'itération sur les propriétés quand hasOwnProperty n'est pas utilisé. Néanmoins, il faut faire attention avec l'utilisation de vieilles librairies comme Prototype qui ne bénéficient pas des nouvelles fonctions d'ECMAScript. Dans ce cadre, écrire des boucles for in sans hasOwnProperty est garanti de causer des erreurs.

    +

    En conclusion

    +

    Il est recommandé de toujours utiliser hasOwnProperty avec ECMAScript 3 ou moins, ou dans du code de librairie. Dans ces environnements, il ne faut jamais assumer que les prototypes natifs n'ont pas été étendus. Depuis ECMAScript 5, Object.defineProperty permet de définir les propriétés non-dénombrables et donc permet d'omettre les appels à hasOwnProperty dans le code de l'application.

    +

    Fonctions

    Déclaration des fonctions et expressions

    Les fonctions en JavaScript sont des objets de première classe. Cela signifie qu'elles peuvent être passées comme toute autre valeur. Une utilisation courante de cette caractéristique est de passer une fonction anonyme comme une fonction de rappel "callback" qui peut être asynchrone.

    +

    La déclaration function

    +
    function foo() {}
    +

    La fonction ci-dessus est hissée "hoisted" avant le démarrage du programme; ainsi, elle est donc disponible partout dans la portée "scope" d'application où la fonction a été définie, même si appelé avant sa définition dans le code source.

    +
    foo(); // Fonctionne car foo a été crée avant l'exécution de ce code
    +function foo() {}
    +

    L'expresssion function

    +
    var foo = function() {};
    +

    Cet exemple attribue une fonction anonyme et sans nom à la variable foo.

    +
    foo; // 'undefined'
    +foo(); // provoque un erreur de type TypeError
    +var foo = function() {};
    +

    En raison du fait que var est une déclaration qui hisse le nom de la variable foo avant que l'exécution réelle du code ne commence, foo est déjà déclarée lorsque le script est exécuté.

    +

    Mais comme les assignements ne se produisent qu'au moment de l'exécution, la valeur de foo sera par défaut mise à undefined avant l'exécution du code.

    +

    L'expression de fonction nommée

    +

    Un autre cas est l'attribution de fonctions nommées.

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

    Ici, bar n'est pas disponible dans la portée externe "outer scope", puisque la fonction est seulement assignée à foo, mais elle est disponible à l'intérieur de bar. Cela est dû à la méthode de résolution de noms de JavaScript: le nom de la fonction est toujours disponible dans la portée locale "local scope" de la fonction elle-même.

    +

    Comment marche this

    Pour JavaScript, ce que le nom spécial this réfère à diffère de la plupart des autres langages de programmation. Il y a exactement cinq façons différente de lier la valeur de this dans le langage.

    +

    Le contexte global "global scope"

    +
    this;
    +

    Lorsque vous utilisez this dans le contexte global, il va simplement référer à l'objet global.

    +

    Appel de fonction

    +
    foo();
    +

    Ici, this va aussi référer à l'objet global.

    + +

    Appel de méthode

    +
    test.foo(); 
    +

    Dans cet exemple, this va référer à test.

    +

    Appel de constructeur

    +
    new foo(); 
    +

    Un appel de fonction qui est précédé par le mot clé new agit comme un constructeur. Dans la fonction, this va référer à un Object nouvellement créé.

    +

    Assignement direct de this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // table va s'étendre comme ci-dessous
    +foo.call(bar, 1, 2, 3); // mène à: a = 1, b = 2, c = 3
    +

    Lorsque vous utilisez les méthodes call (appeler) ou apply (appliquer) de Function.prototype, la valeur de this à l'intérieur de la fonction appelée est directement définie par le premier argument de l'appel correspondant.

    +

    En conséquence, dans l'exemple ci-dessus le cas d'appel de méthode ne s'applique pas, et this à l'intérieur de foo va bien référer à bar.

    + +

    Pièges communs

    +

    Bien que la plupart de ces cas ont du sens, le premier cas peut être considéré comme une autre faute de design du langage, car il n'est jamais d'aucune utilité pratique.

    +
    Foo.method = function() {
    +    function test() {
    +        // this réfère à l'objet global
    +    }
    +    test();
    +};
    +

    Une autre erreur souvent commise est que this l'intérieur de test se réfère à foo; ce qui n'est pas du tout le cas.

    +

    Pour accéder à foo de l'intérieur de test, vous pouvez créer une variable locale à intérieur de method qui fait référence à foo.

    +
    Foo.method = function() {
    +    var self = this;
    +    function test() {
    +        // Utilisez self au lieu de this ici
    +    }
    +    test();
    +};
    +

    self est juste une variable normale, couramment utilisée pour référencer un this extérieur. Combiné avec des fermetures "closures", on peut l'utiliser pour passer les valeurs de this.

    +

    À partir d'ECMAScript 5, l'utilisation de la méthode bind avec une fonction anonyme mène au même resultat:

    +
    Foo.method = function() {
    +    var test = function() {
    +        // maintenant, this réfère à Foo
    +    }.bind(this);
    +    test();
    +};
    +

    Assignement de méthodes

    +

    Une autre chose qui ne marche pas en JavaScript est l'alias de fonction, ou l'assignement d'une méthode à une variable.

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

    En raison du premier des cinq cas, test agit maintenant comme un appel de fonction normal; par conséquent, this à l'intérieur de la fonction ne va plus référer à someObject.

    +

    Bien que la liaison tardive "late binding" de this pouvait sembler comme une mauvaise idée au premier abord, c'est en fait grâce à cela que l'héritage prototypique fonctionne.

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

    Quand method est appelée d'une instance de bar, this va référer à cette même instance.

    +

    Fermetures et réferences

    Les fermetures "closures" sont une des fonctionnalités les plus puissantes de JavaScript. +Avec les fermetures, les portées gardent toujours l'accès à la portée externe, dans laquelle elles ont été définies. +Puisque la seule portée que JavaScript a est la portée de fonction, toutes les fonctions, par défaut, agissent comme des fermetures.

    +

    Simuler les variables privées

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

    Ici, Counter retourne deux fermetures: la fonctionincrement ainsi que la fonction get. Ces deux fonctions conservent une référence à la portée de Counter et, par conséquent, gardent toujours l'accès à la variable count qui a été définie dans cette portée.

    +

    Comment marchent les variables privées

    +

    Comme il ne est pas possible de référencer ou assigner des portées en JavaScript, il n'y a aucun moyen d'accéder à la variable count de l'extérieur. +La seule façon d'interagir avec elle est par l'intermédiaire des deux fermetures.

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

    Le code ci-dessus ne va pas changer la variable count dans la portée de Counter, car foo.hack n'a pas été défini dans cette portée. En fait, une nouvelle variable va etre crée - ou va remplacer - la variable globale count.

    +

    Fermetures dans les boucles

    +

    Une erreur souvent commise est d'utiliser les fermetures à l'intérieur de boucles comme si elles copiaient la valeur de la variable d'indice de la boucle.

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

    La programme ci-dessus ne vas pas produire les numéros 0 à 9, il imprimera 10 dix fois.

    +

    La fonction anonyme garde une référence à i. Au moment où console.log est appelée, la boucle for est déjà achevée, et donc la valeur de i est à 10.

    +

    Afin d'obtenir le comportement souhaité, il est nécessaire de créer une copie de la valeur de i.

    +

    Eviter le problème de référence

    +

    Pour copier la valeur de la variable d'index de la boucle, il est préférable d'utiliser une enveloppe anonyme "wrapper".

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

    La fonction externe anonyme est appelée immédiatement avec i en tant que premier argument, et donc le paramètre e recevra une copie de la valeur de i.

    +

    La fonction anonyme qui est passé à setTimeout a maintenant une référence à e, dont la valeur ne peut pas être changée par la boucle.

    +

    Une autre façon de faire est de retourner une fonction de l'enveloppe anonyme qui aura alors le même comportement que le code ci-dessus.

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

    Une autre façon populaire d'achever le même comportement est d'ajouter un argument supplémentaire à la fonction setTimeout. La fonction passera ces arguments à la fonction de rappel "callback".

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

    Sachez que certains environnements JS (Internet Explorer 9 et avant) ne supportent pas cette dernière approche.

    +

    Enfin, une dernière façon de faire et d'utiliser bind, qui peut lier le contexte this et les arguments pour la fonction.

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

    L'objet arguments

    Chaque portée "scope" de fonction en JavaScript peut accéder à la variable spéciale arguments. +Cette variable contient une liste de tous les arguments qui ont été passés à la fonction.

    + +

    L'objet arguments n'est pas un tableau Array. Même s'il a la sémantique d'un tableau - à savoir la propriété length (longueur) - il n'hérite pas de +Array.prototype mais est en fait un Object.

    +

    Pour cette raison, il n'est pas possible d'utiliser les méthodes de tableau standards comme push, pop ou slice sur arguments. +Bien qu'itérer avec une boucle for fonctionne, il est nécessaire de convertir la variable arguments en un véritable Array pour pouvoir lui appliquer les fonctions de tableau Array standards.

    +

    Conversion à Array

    +

    Le code ci-dessous va retourner un nouveau tableau Array contenant tous les éléments de l'objet arguments.

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

    Cette conversion est lente, il n'est donc pas recommandé de l'utiliser dans des sections de code où la performance est critique.

    +

    Passage d'arguments

    +

    Voici la méthode recommandée pour passer des arguments d'une fonction à une autre.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // faire qqch ici
    +}
    +

    Une autre astuce consiste à utiliser à la fois call et apply pour transformer des méthodes - fonctions qui utilisent la +valeur de this ainsi que leurs arguments - en des fonctions normales qui n'utilisent que leurs arguments.

    +
    function Person(first, last) {
    +  this.first = first;
    +  this.last = last;
    +}
    +
    +Person.prototype.fullname = function(joiner, options) {
    +  options = options || { order: "western" };
    +  var first = options.order === "western" ? this.first : this.last;
    +  var last =  options.order === "western" ? this.last  : this.first;
    +  return first + (joiner || " ") + last;
    +};
    +
    +// Créer une version non liée de "fullname", utilisable sur n'importe quel
    +// objet avec les propriétés 'first' et 'last' passées comme premier
    +// argument. Cette enveloppe n'aura pas besoin de changer si fullname
    +// change le nombre ou l'ordre des ses arguments.
    +Person.fullname = function() {
    +  // résultat: Person.prototype.fullname.call(this, joiner, ..., argN);
    +  return Function.call.apply(Person.prototype.fullname, arguments);
    +};
    +
    +var grace = new Person("Grace", "Hopper");
    +
    +// 'Grace Hopper'
    +grace.fullname();
    +
    +// 'Turing, Alan'
    +Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
    +

    Paramètres formels et arguments indexés

    +

    L'objet arguments crée des fonctions getter et setter à la fois pour ses propriétés et les paramètres formels de la fonction.

    +

    Par conséquent, changer la valeur d'un paramètre formel va également modifier la valeur de la propriété correspondante sur l'objet arguments, et vice-versa.

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

    Mythes et faits sur la performance

    +

    Le seul moment où l'objet arguments n'est pas créé est quand il est déclaré comme un nom à l'intérieur d'une fonction ou l'un de ses paramètres formels. Le fait qu'il soit utilisé ou non n'est pas important.

    +

    Les deux getter et setter sont toujours créé; et donc l'utilisation d'arguments n'a aucune incidence sur la performance.

    + +

    Cependant, un cas va considérablement réduire la performance des moteurs JavaScript modernes. +C'est le cas de l'utilisation de arguments.callee.

    +
    function foo() {
    +    arguments.callee; // faire quelque chose avec cet objet de fonction
    +    arguments.callee.caller; // et la fonction appelante
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Seraient normalement inline...
    +    }
    +}
    +

    Dans le code ci-dessus, foo ne peut plus être inline car il a besoin de se connaitre lui-même et connaitre son appelant. +Cela défait les gains possibles de performance qui découleraient d'inline, mais cela casse également l'encapsulation +car la fonction peut maintenant être dépendante d'un contexte d'appel spécifique.

    +

    Utiliser arguments.callee ou l'une de ses propriétés est fortement déconseillé.

    + +

    Constructeurs

    Les constructeurs en JavaScript diffèrent de beaucoup d'autres langages. +Tout appel de fonction précédé par le mot clé new agit comme un constructeur.

    +

    Dans le constructeur - la fonction appelée - la valeur de this se réfère à un objet nouvellement créé. +Le prototype de ce nouvel objet pointe sur le prototype de l'objet de fonction qui a été invoqué comme constructeur.

    +

    Si la fonction qui a été appelée n'a pas de déclaration return explicite, elle renvoira implicitement la valeur de this - le nouvel objet.

    +
    function Person(name) {
    +    this.name = name;
    +}
    +
    +Person.prototype.logName = function() {
    +    console.log(this.name);
    +};
    +
    +var sean = new Person();
    +

    Le code ci-dessus appelle Person en tant que constructeur et définit le prototype du nouvel objet créé à Person.prototype.

    +

    En cas d'une déclaration return explicite, la fonction renvoie la valeur spécifiée par cette déclaration, mais seulement si cette valeur est un objet Object.

    +
    function Car() {
    +    return 'ford';
    +}
    +new Car(); // un nouvel objet, pas 'ford'
    +
    +function Person() {
    +    this.someValue = 2;
    +
    +    return {
    +        name: 'Charles'
    +    };
    +}
    +new Test(); // l'objet retourné ({name:'Charles'}) n'inclue pas someValue
    +

    Lorsque le mot clé new est omis, la fonction ne retournera pas un nouvel objet.

    +
    function Pirate() {
    +    this.hasEyePatch = true; // this est l'object global!
    +}
    +var somePirate = Pirate(); // somePirate est undefined
    +

    Bien que l'exemple ci-dessus a l'air de marcher, il utilisera l'objet global pour la valeur de this, en raison du fonctionnement particulier de this en JavaScript.

    +

    Fabriques

    +

    Pour pouvoir omettre le mot clé new, la fonction constructeur doit retourner explicitement une valeur.

    +
    function Robot() {
    +    var color = 'gray';
    +    return {
    +        getColor: function() {
    +            return color;
    +        }
    +    }
    +}
    +Robot.prototype = {
    +    someFunction: function() {}
    +};
    +
    +new Robot();
    +Robot();
    +

    Les deux appels à Robot retournent la même chose, un objet nouvellement créé qui possède une propriété appelée getColor, qui est une fermeture "closure".

    +

    Il convient également de noter que l'appel new Robot() n'affecte pas le prototype de l'objet retourné. +Bien que le prototype sera mis sur le nouvel objet créé, Robot ne retourne jamais cet objet.

    +

    Dans l'exemple ci-dessus, il n'y a pas de différence fonctionnelle entre l'utilisation et la non-utilisation du mot clé new.

    +

    Creation de nouvels objects via fabriques

    +

    Il est souvent recommandé de ne pas utiliser new car l'oublier peut conduire à des bugs.

    +

    Pour créer un nouvel objet, il faut plutôt utiliser une fabrique qui va construire un nouvel objet.

    +
    function CarFactory() {
    +    var car = {};
    +    car.owner = 'nobody';
    +
    +    var milesPerGallon = 2;
    +
    +    car.setOwner = function(newOwner) {
    +        this.owner = newOwner;
    +    }
    +
    +    car.getMPG = function() {
    +        return milesPerGallon;
    +    }
    +
    +    return car;
    +}
    +

    Bien que le code qui précède est robuste contre un mot clé new manquant et rend certainement +l'utilisation de variables privées plus facile, il y a des inconvénients.

    +
      +
    1. Il utilise plus de mémoire car les objets créés ne partagent pas leurs méthodes avec un prototype.
    2. +
    3. Pour hériter, la fabrique a besoin de copier toutes les méthodes de l'autre objet ou mettre l'autre objet sur le prototype du nouvel objet.
    4. +
    5. Abandonner la chaîne de prototype à cause d'un mot clé new laissé de côté est contraire à l'esprit du langage.
    6. +
    +

    En Conclusion

    +

    Omettre le mot clé new peut conduire à des bugs, mais ce n'est certainement pas une raison d'abandonner l'utilisation des prototypes. +En fin de compte il s'agit de savoir quelle solution est la mieux adaptée pour les besoins de l'application. +Il est particulièrement important de choisir un style spécifique de création d'objet et toujours l'utiliser afin de rester cohérent.

    +

    Portées "scopes" et espaces de noms "namespaces"

    Bien que JavaScript utilise une syntaxe avec accolades pour les blocs, il ne crée pas de portée "scope" de bloc; par conséquent, la seule portée du langage est la portée de fonction.

    +
    function test() { // une portée "scope"
    +    for(var i = 0; i < 10; i++) { // pas une portée 
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    + +

    Il n'existe pas d'espaces de noms "namespaces" en JavaScript, ce qui signifie que tout est défini dans un espace de noms commun partagé par tous.

    +

    Chaque fois qu'une variable est référencée, JavaScript va traverser vers le haut toutes les portées jusqu'à ce qu'il la trouve. +S'il atteint la portée globale sans avoir trouvé le nom demandé, il va générer une erreur de référence ReferenceError.

    +

    Le fléau des variables globales

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

    Les deux scripts ci-dessus n'ont pas le même effet. Le script A définit une variable appelée foo dans la portée globale, le script B définit foo dans la portée actuelle.

    +

    Ne pas utiliser var peut avoir des répercussions majeures.

    +
    // portée globale
    +var foo = 42;
    +function test() {
    +    // portée locale
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    En laissant de côté la déclaration var à l'intérieur de la fonction test, on remplace la valeur de foo. +Même si au premier abord cela ne semble pas être une grosse affaire, des milliers de lignes de JavaScript qui n'utilisent pas var créeront des bogues horribles qui seront très difficiles à dépister.

    +
    // portée globale
    +var items = [/* some list */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // portée de subLoop
    +    for(i = 0; i < 10; i++) { // var manquant
    +        // ici, des choses incroyables!
    +    }
    +}
    +

    La boucle externe se terminera après le premier appel à subLoop, car subLoop écrase la valeur globale de i. +L'utilisation d'un var pour la deuxième boucle for aurait facilement évité cette erreur. +La déclaration de var devrait jamais être laissé de côté, sauf si l'effet désiré est d'affecter la portée externe.

    +

    Variables locales

    +

    Seuls les paramètres de fonction et les variables déclarées avec un var peuvent créer des variables locales en JavaScript.

    +
    // portée globale
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // portée locale de la fonction test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    foo et i sont bien des variables locales à l'intérieur de la portée de la fonction test, mais l'assignment bar remplacera la variable globale portant le même nom.

    +

    Remontée "hoisting"

    +

    JavaScript hisse les déclarations. Cela signifie que les déclarations de var et function seront déplacés vers le haut de leur portée englobante.

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

    Le code ci-dessus est transformé avant que l'exécution ne commence. JavaScript déplace les déclarations var, ainsi que les déclarations function, vers le haut de la portée la plus proche.

    +
    // les déclarations var sont maintenant ici
    +var bar, someValue; // mis à 'undefined' par défaut
    +
    +// les déclarations de fonction aussi
    +function test(data) {
    +    var goo, i, e; // pas de portée de bloc,
    +                   // donc déclarations var viennent ici
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // échoue avec TypeError puisque bar est toujours 'undefined'
    +someValue = 42; // les assignements ne sont pas concernés par la remontée
    +bar = function() {};
    +
    +test();
    +

    L'inexistence des portées de bloc va non seulement déplacer les déclarations var en dehors du corps des boucles, mais va aussi rendre les résultats de certaines constructions de if non-intuitifs.

    +

    Dans le code original, la déclaration if semblait modifier la variable globale goo, alors qu'en fait elle modifiait la variable locale - après la remontée appliquée.

    +

    Sans la connaissance du concept de remontée, on pourrait soupçonner que le code ci-dessous produirait une erreur de référence ReferenceError.

    +
    // verifie si SomeImportantThing a bien été initializé
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Le code fonctionne pourtant bien, car la déclaration de var est déplacé vers le haut de la portée globale.

    +
    var SomeImportantThing;
    +
    +// du code peut, ou pas, initializer SomeImportantThing ici
    +
    +// soyons en sûr
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Ordre de la résolution de noms

    +

    Toutes les portées en JavaScript, y compris la portée globale, ont le nom spécial this défini qui se réfère à l'objet courant.

    +

    Les portées de fonction ont aussi le nom arguments défini qui contient les arguments qui ont été transmis à la fonction.

    +

    Par exemple, lorsque vous essayez d'accéder à une variable nommé foo l'intérieur de la portée d'une fonction, JavaScript va chercher le nom dans l'ordre suivant:

    +
      +
    1. Si il y a une déclaration var foo var dans la portée courante, l'utiliser.
    2. +
    3. Si l'un des paramètres de la fonction est nommé foo, l'utiliser.
    4. +
    5. Si la fonction elle-même est appelée foo, l'utiliser.
    6. +
    7. Sinon, accéder à la portée externe suivante, et recommencer à #1 pour cette portée.
    8. +
    +

    Remarque: Avoir un paramètre appelé arguments va empêcher la création d'objet par défaut arguments.

    +

    Espaces de noms

    +

    Le fait de n'avoir qu'un seul espace de noms global engendre un risque de conflit de noms de variables, un problème commun en JavaScript. +En JavaScript, ce problème peut facilement être évité grâces aux enveloppes anonymes.

    +
    (function() {
    +    // un "espace de nom" autonome
    +
    +    window.foo = function() {
    +        // une fermeture exposée
    +    };
    +
    +})(); // exécute la fonction immédiatement
    +

    Les fonctions anonymes sont considérées comme des expressions; ainsi elles doivent d'abord être évaluées avant d'être appelées.

    +
    ( // évaluer la fonction à l'intérieur des parenthèses
    +function() {}
    +) // et retourner la fonction object
    +() // appeler le résultat de l'évaluation
    +

    Il y a d'autres façons d'évaluer et d'appeler directement l'expression de fonction qui, bien que différentes dans la syntaxe, se comportent de la même manière.

    +
    // Autres styles d'invocation directe
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +// etc.
    +

    En conclusion

    +

    Il est recommandé de toujours utiliser une enveloppe anonyme pour encapsuler du code dans son propre espace de noms. +Non seulement cela protège des conflits de noms de code, cela permet également une meilleure modularisation des programmes.

    +

    En outre, l'utilisation de variables globales est considéré comme une mauvaise pratique. +Leur utilisation indique un code mal écrit, sujet à des erreurs, et difficile à maintenir.

    +

    Tableaux

    Tableaux: iteration et propriétés

    Bien que les tableaux soient des objets en JavaScript, il n'y a pas de bonnes raisons d'utiliser la boucle for in. +En fait, il y a un certain nombre de bonnes raisons contre l'utilisation de for in sur les tableaux.

    +

    Remarque: Les tableaux JavaScript ne sont pas associatifs. JavaScript n'offre que les objets pour associer des clés à des valeurs. Contrairement aux tableaux associatifs, les objets ne préservent pas l'ordre.

    +

    La boucle for in énumère toutes les propriétés qui sont sur la chaîne de prototypes, et le seul moyen d'exclure ces propriétés consiste à utiliser +hasOwnProperty, par conséquent la boucle for in est vingt fois plus lente qu'une boucle for classique.

    +

    Itération

    +

    Pour itérer sur les tableaux de façon performante, il est préférable d'utiliser la boucle for classique.

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

    Notez l'optimization supplémentaire dans l'exemple ci-dessus: la longueur du tableau est mise en mémoire "cached" via l = list.length.

    +

    La propriété length est définie sur le tableau lui-même, mais la rechercher à chaque itération de la boucle à un coût. +Bien que les moteurs JavaScript récents peuvent appliquer l'optimisation, il n'y a aucun moyen de savoir si le code s'exécutera sur un de ces nouveaux moteurs.

    +

    En effet, mettre la longueur du tableau en mémoire cache peut doubler la vitesse d'execution de la boucle.

    +

    La propriété length

    +

    Le getter de la propriété length (longueur) renvoie simplement le nombre d'éléments contenus dans le tableau, mais le setter peut être utilisé pour +tronquer le tableau.

    +
    var arr = [1, 2, 3, 4, 5, 6];
    +arr.length = 3;
    +arr; // [1, 2, 3]
    +
    +arr.length = 6;
    +arr.push(4);
    +arr; // [1, 2, 3, undefined, undefined, undefined, 4]
    +

    Attribuer une longueur inférieure tronque le tableau. Accroître la longueur crée un tableau clairsemé.

    +

    En conclusion

    +

    Pour de meilleures performances, il est recommandé de toujours utiliser la boucle for classique et de mettre en mémoire la propriété length. +L'utilisation de la boucle for in sur un tableau est un signe de code mal écrit, de mauvaise performance, et sujet à des bogues.

    +

    Le constructeur Array

    Le constructeur Array traite ses paramètres de façon ambigu. +Il est fortement recommandé d'utiliser le littéral de tableau - notation [] - pour créer de nouveaux tableaux.

    +
    [1, 2, 3]; // Résultat: [1, 2, 3]
    +new Array(1, 2, 3); // Résultat: [1, 2, 3]
    +
    +[3]; // Résultat: [3]
    +new Array(3); // Résultat: []
    +new Array('3') // Résultat: ['3']
    +

    Dans les cas où il n'y a qu'un seul argument passé au constructeur Array, et quand cet argument est un nombre Number, le constructeur va retourner un nouveau tableau clairsemé avec la propriété length (longueur) fixée à la valeur de l'argument. +Il faut noter que de cette façon, seulement la propriété length du nouveau tableau sera mise en place, les indices réels du tableau ne seront pas initialisés.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // faux, l'indice n'existe pas
    +

    Être en mesure de régler la longueur du tableau à l'avance n'est utile que dans quelques cas, comme la répétition d'une chaîne de caractères, dans lequel on évite l'utilisation d'une boucle.

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

    En conclusion

    +

    Les littéraux sont préférés au constructeur Array. Ils sont plus courts, ont une syntaxe plus claire, et augmente la lisibilité du code.

    +

    Types

    Égalité et comparaisons

    JavaScript a deux façons de comparer les valeurs des objets pour égalité.

    +

    L'opérateur d'égalité

    +

    L'opérateur d'égalité se compose de deux signes égal: ==.

    +

    JavaScript est un langage au typage faible "weak typing". Cela signifie que l'opérateur d'égalité va convertir les deux opérandes en un même type afin de les comparer.

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

    Le tableau ci-dessus montre les résultats de la coercition de type, et c'est la raison principale pourquoi l'utilisation de == est largement considéré comme une mauvaise pratique. Les règles de conversion de types compliquées introduisent des bogues difficiles à dépister.

    +

    L'opérateur d'égalité stricte

    +

    L'opérateur d'égalité stricte se compose de trois signes égal: ===.

    +

    Il fonctionne comme l'opérateur d'égalité normale, sauf que l'égalité stricte ne converti pas le types de ses opérandes.

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

    Les résultats ci-dessus sont beaucoup plus clairs et permettent la rupture précoce de code. +Cela durcit le code jusqu'à un certain degré, et améliore la performance dans le cas où les opérandes sont de types différents.

    +

    Comparaison d'objets

    +

    Bien que == et === sont appelés opérateurs d'égalité, ils se comportent différement quand au moins un des opérandes est un objet Object.

    +
    {} === {};                   // faux
    +new String('foo') === 'foo'; // faux
    +new Number(10) === 10;       // faux
    +var foo = {};
    +foo === foo;                 // vrai
    +

    En effet, les deux opérateurs comparent l'identité et non pas l'égalité. Autrement dit, ils comparent pour la même instance de l'objet, tout comme is en Python, ou la comparaison de pointeur en C.

    +

    En conclusion

    +

    Il est fortement recommandé de n'utiliser que l'opérateur d'égalité stricte. Dans les cas où les types ont à être convertis, cela devraient être fait explicitement et non pas laissé aux règles complexes de coercition de type du langage.

    +

    L'opérateur typeof

    L'opérateur typeof (avec instanceof) est probablement le plus grand défaut de design de JavaScript, car il est presque complètement cassé.

    +

    Bien que instanceof (instance de) a quelques utilisations limitées, typeof (type de) n'a qu'un seul cas pratique d'utilisation, et ce cas n'est pas la vérification du type d'un objet.

    + +

    table de types JavaScript

    +
    Valeur              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 pour Nitro/V8)
    +new RegExp("meow")  RegExp     object (function pour Nitro/V8)
    +{}                  Object     object
    +new Object()        Object     object
    +

    Dans la table ci-dessus, Type se réfère à la valeur retournée pas l'opérateur typeof, et comme on peut le voir clairement, cette valeur est incohérente.

    +

    Class se réfère à la valeur de la propriété interne [[Class]] d'un objet.

    + +

    Pour récupérer la valeur de [[Class]], on peut utiliser la méthode toString de Object.prototype.

    +

    La classe d'un objet

    +

    La spécification donne exactement un moyen d'accéder à la valeur [[Class]]: via 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'); // vrai
    +is('String', new String('test')); // vrai
    +

    Dans l'exemple ci-dessus, Object.prototype.toString est appelé avec la valeur this pointant sur l'objet dont on cherche a récupérer la valeur de [[Class]].

    + +

    Test pour les variables indéfinies

    +
    typeof foo !== 'undefined'
    +

    Ce qui précède vérifie si foo a été effectivement déclarée. Juste référencer la variable résulterait en une erreur de référence ReferenceError. C'est la seule chose pour laquelle typeof est réellement utile.

    +

    En conclusion

    +

    Pour vérifier le type d'un objet, il est fortement recommandé d'utiliser Object.prototype.toString parce que c'est le seul moyen fiable de le faire. +Comme représenté dans la table de type ci-dessus, certaines valeurs de retour de typeof ne sont pas définies dans la spécification; et donc, elles peuvent différer entre les implémentations.

    +

    If faut éviter d'utiliser typeof, sauf pour vérifier si une variable est définie.

    +

    L'opérateur instanceof

    L'opérateur instanceof (instance de) compare les constructeurs de ses deux opérandes. Il est seulement utile pour comparer des objets faits sur mesure. Utilisé sur les types intégrés, il est +aussi inutile que l'opérateur typeof.

    +

    Comparer des objets personnalisés

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // vrai
    +new Bar() instanceof Foo; // vrai
    +
    +// Ceci définit simplement Bar.prototype à l'objet de fonction Foo,
    +// mais pas à une instance réelle de Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // faux
    +

    Utiliser instanceof avec des types natifs

    +
    new String('foo') instanceof String; // vrai
    +new String('foo') instanceof Object; // vrai
    +
    +'foo' instanceof String; // faux
    +'foo' instanceof Object; // faux
    +

    Une chose importante à noter ici est que instanceof ne fonctionne pas sur les objets qui proviennent de différents contextes JavaScript (par exemple, différents documents +dans un navigateur web), car leurs constructeurs ne seront pas exactement le même objet.

    +

    En conclusion

    +

    L'opérateur instanceof devrait seulement être utilisé sur des objets crées sur mesure provenant du même contexte JavaScript. +Tout comme l'opérateur typeof, chaque autre utilisation de celui-ci devrait être évitée.

    +

    Changements de types

    JavaScript est un langage faiblement typé, il appliquera la coercition de type partout où c'est possible.

    +
    // Ceux-ci sont vrais
    +new Number(10) == 10; // Objet Number est converti
    +                      // en un nombre primitif via un appel implicite
    +                      // à la méthode Number.prototype.valueOf
    +
    +10 == '10';           // Strings est converti en Number
    +10 == '+10 ';         // Encore aussi fou
    +10 == '010';          // Et encore
    +isNaN(null) == false; // null est converti en 0
    +                      // ce qui, bien sûr, n'est pas NaN
    +
    +// Ceux-ci sont faux
    +10 == 010;
    +10 == '-10';
    + +

    Pour éviter les problèmes ci-dessus, l'utilisation de l'opérateur d'égalité stricte est fortement recommandé. +Bien que cela évite beaucoup de pièges communs, il y en reste encore beaucoup. Tous ces pièges découlent de la faiblesse du système de typage de JavaScript.

    +

    Constructeurs de types internes

    +

    Les constructeurs de types internes comme Number et String se comportent différemment suivant s'ils sont utilisés avec ou sans le mot clé new.

    +
    new Number(10) === 10;     // Faux, Object et Number
    +Number(10) === 10;         // Vrai, Number et Number
    +new Number(10) + 0 === 10; // Vrai, due à la reconversion implicite
    +

    L'utilisation d'un type intégré comme Number en tant que constructeur va créer une nouvel objet Number. Mais sans le mot clé new, Number se comportera comme un convertisseur.

    +

    De plus, passer des valeurs littérales ou des non-objets se traduira par encore plus de coercition de type.

    +

    La meilleure option est de forcer explicitement le type à l'un des trois types possibles .

    +

    Forcer à String

    +
    '' + 10 === '10'; // vrai
    +

    En faisant précéder une chaîne vide, une valeur peut facilement être converti en une chaîne.

    +

    Forcer à Number

    +
    +'10' === 10; // vrai
    +

    L'utilisation de l'opérateur plus unaire converti une valeur en nombre.

    +

    Forcer à Boolean

    +

    L'utilisation double de l'opérateur non converti une valeur en booléen.

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

    Cœur

    Il ne faut pas utiliser eval

    La fonction eval exécute une chaîne de caractères représentant du code JavaScript dans la portée locale.

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

    Cependant, eval n'exécute dans la portée locale que quand il est appelé directement et quand le nom de la fonction appelée est en fait eval.

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

    L'utilisation de la fonction eval doit être évitée. 99,9% de ses "cas d'utilisation" peuvent être obtenues sans elle.

    +

    eval déguisé

    +

    Les fonctions timeout setTimeout et setInterval acceptent une chaîne comme premier argument. +Cette chaîne sera toujours exécutée dans la portée globale car dans ce cas, eval n'est pas appelé directement.

    +

    Problèmes de sécurité

    +

    eval est aussi un problème de sécurité, car il exécute n'importe quel code qu'on lui donne. +Il devrait jamais être utilisé avec des chaînes d'origines inconnues ou douteuses.

    +

    En conclusion

    +

    eval ne devrait jamais être utilisé. Sa presence met en doute le fonctionnement, la performance, et la sécurité du code qui l'utilise. +Si quelque chose a besoin d'eval pour pouvoir fonctionner, il ne doit pas être utilisé en premier lieu. Un meilleur design qui n'utilise pas eval doit être trouvé et implementé.

    +

    undefined et null

    JavaScript a deux valeurs distinctes pour "rien": null et undefined, undefined étant la plus utile.

    +

    La valeur undefined

    +

    undefined est un type avec exactement une valeur:undefined.

    +

    Le langage définit également une variable globale qui a la valeur undefined. Cette variable est aussi appelée undefined. Cependant, cette variable n'est ni une constante, ni un mot clé du langage, ce que signifie que sa valeur peut être facilement écrasée.

    + +

    Voici quelques exemples de cas où la valeur undefined est retournée:

    +
      +
    • Accès à la variable globale (non modifié) undefined.
    • +
    • Accès à une variable déclarée, mais pas encore initialisée.
    • +
    • Retours implicites de fonctions sans déclaration return.
    • +
    • Déclarations return vides, qui ne renvoient rien.
    • +
    • Recherches de propriétés inexistantes.
    • +
    • Paramètres de fonction qui ne ont pas de valeur explicite passée.
    • +
    • Tout ce qui a été mis à la valeur de undefined.
    • +
    • Toute expression sous forme de void(expression).
    • +
    +

    Changements à la valeur de undefined

    +

    Puisque la variable globale undefined contient uniquement une copie de la valeur réelle undefined, l'attribution d'une nouvelle valeur à la variable ne modifie pas la valeur du type undefined.

    +

    Pourtant, pour pouvoir comparer quelque chose contre la valeur de undefined, il est d'abord nécessaire pour récupérer la valeur de undefined.

    +

    Afin de protéger le code contre une éventuelle variable undefined écrasée, une technique commune utilisée consiste à ajouter un paramètre supplémentaire à une enveloppe anonyme et de lui passer aucun argument.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined dans la portée locale 
    +    // réfère bien à la valeur `undefined`
    +
    +})('Hello World', 42);
    +

    Une autre façon d'obtenir le même effet est d'utiliser une déclaration à l'intérieur de l'enveloppe.

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

    La seule différence étant quelques caractères de plus pour écrire "var".

    +

    Utilisation de null

    +

    Alors que undefined dans le contexte du langage JavaScript est utilisé dans la plupart des cas dans le d'un null traditionnel, le null réel (un littéral et un type) est juste un autre type de données.

    +

    null est utilisé par JavaScript (comme signaler la fin de la chaîne de prototypes avec Foo.prototype = null), mais dans presque tous les cas, il peut être remplacé par undefined.

    +

    Insertion automatique du point-virgule

    Bien que JavaScript a une syntaxe de style C, il n'impose pas les points-virgules dans le code source. Il est donc possible de les omettre.

    +

    JavaScript n'est pas un langage sans points-virgules. En fait, les points-virgules sont necessaires pour comprendre le code source. Par conséquent, l'analyseur JavaScript les insère automatiquement chaque fois qu'il rencontre une erreur d'analyse due à un point-virgule manquant.

    +
    var foo = function() {
    +} // erreur d'analyse, point-virgule attendu
    +test()
    +

    L'analyseur insère un point-virgule, puis tente à nouveau.

    +
    var foo = function() {
    +}; // plus d'error, l'analyse continue
    +test()
    +

    L'insertion automatique du point-virgule est considérée comme l'un des plus gros défauts de conception dans le langage parce que cela peut changer le comportement du code.

    +

    Comment cela marche

    +

    Le code ci-dessous n'a pas de points-virgules, l'analyseur va donc décider où les insérer.

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

    Voici le résultat du jeu de devinette de l'analyseur.

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // pas inséré, les lignes ont fusionné
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- inséré
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- inséré
    +
    +        return; // <- inséré, casse la déclaration return
    +        { // traité comme un bloc
    +
    +            // un label et une déclaration d'expression
    +            foo: function() {} 
    +        }; // <- inséré
    +    }
    +    window.test = test; // <- inséré
    +
    +// les lignes ont fusionné ici encore
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- inséré
    +
    +})(window); //<- inséré
    + +

    L'analyseur a radicalement changé le comportement du code ci-dessus. Dans certains cas, il fait la mauvaise chose.

    +

    Parenthèse en tête

    +

    En cas de parenthèse en tête, l'analyseur ne va pas insérer de point-virgule.

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

    Ce code fusionne en une ligne.

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

    Il y a de très fortes chances que log ne retourne pas de fonction; par conséquent, le programme ci-dessus va produire une erreur de type TypeError indiquant que undefined n'est pas un function undefined is not a function.

    +

    En conclusion

    +

    Il est fortement recommandé de ne jamais omettre les points-virgules. Il est également recommandé de garder les accolades sur la même ligne que leurs déclarations correspondantes et de ne jamais les omettre pour les déclaration en une ligne if / else. Ces mesures vont non seulement améliorer la cohérence du code, mais elles empêcheront également l'analyseur JavaScript de changer le comportement du code.

    +

    L'opérateur delete

    Il est impossible de supprimer les variables globales, fonctions et autres choses qui ont l'attribut DontDelete en JavaScript.

    +

    Le code global et le code de fonction

    +

    Quand une variable ou une fonction est définie dans la portée globale ou une portée de fonction, c'est une propriété soit de l'objet d'activation, soit de l'objet global. +Ces propriétés ont un ensemble d'attributs, dont l'un est DontDelete. Les déclarations de variables et de fonctions dans le code global et le code de fonction vont toujours créer des propriétés avec DontDelete, elle ne peuvent donc pas être supprimées.

    +
    // global variable:
    +var a = 1; // DontDelete est mis
    +delete a; // faux
    +a; // 1
    +
    +// normal function:
    +function f() {} // DontDelete is mis
    +delete f; // faux
    +typeof f; // "function"
    +
    +// reassigner n'aide pas:
    +f = 1;
    +delete f; // faux
    +f; // 1
    +

    Propriétés explicites

    +

    Les propriétés crées explicitement peuvent être supprimées normalement.

    +
    // propriété crée explicitement:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // vrai
    +delete obj.y; // vrai
    +obj.x; // undefined
    +obj.y; // undefined
    +

    Dans l'exemple ci-dessus, les propriétés obj.x et obj.y peuvent être supprimées parce qu'elles n'ont pas l'attribut DontDelete. C'est aussi pourquoi l'exemple ci-dessous fonctionne également.

    +
    // ceci fonctionne, sauf sur IE:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // vrai - juste une var globale
    +delete GLOBAL_OBJECT.a; // vrai
    +GLOBAL_OBJECT.a; // undefined
    +

    Ici, nous utilisons une astuce pour supprimer a. this se réfère ici à l'objet global et nous déclarons explicitement la variable a comme sa propriété, ce qui nous permet de la supprimer.

    +

    IE (au moins 6-8) a quelques bogues, le code ci-dessus n'y fonctionne pas.

    +

    Les arguments de fonction et built-ins

    +

    Les arguments normaux de fonctions, objets arguments et les propriétés intégrées "built-in" ont aussi l'attribut DontDelete.

    +
    // les arguments de fonction et les propriétés:
    +(function (x) {
    +
    +  delete arguments; // faux
    +  typeof arguments; // "object"
    +
    +  delete x; // faux
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // faux
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    Objets hôtes

    +

    Le comportement de l'opérateur delete peut être imprévisible pour les objets hébergés "hosted". Dû à la spécification, les objets hôte sont autorisés à mettre en œuvre tout type de comportement.

    +

    En conclusion

    +

    L'opérateur delete a souvent un comportement inattendu et ne peut être utilisé que pour supprimer les propriétés explicitement définies sur des objets normaux.

    +

    Autres

    setTimeout et setInterval

    Puisque JavaScript est asynchrone, il est possible de programmer l'exécution future d'une fonction en utilisant les fonctions setTimeout et setInterval.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // retourne un nombre > 0
    +

    Quand setTimeout est appelé, il renvoie l'identifiant de la temporisation et fixe la date de d'exécution de foo approximativement mille millisecondes dans le future. foo sera exécuté une seule fois.

    +

    La résolution de l'horloge du moteur JavaScript exécutant le code, le fait que JavaScript est mono-thread, et la possibilité qu'autre code en cours d'exécution peut bloquer le fil "thread", font qu'il n'est pas possible de déterminer le temps exact d'attente spécifié dans l'appel setTimeout.

    +

    La fonction passée en tant que premier paramètre sera appelé par l'objet global, ce qui signifie que this à l'intérieur de la fonction appelée fait référence à l'objet global.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this réfère a l'boject global
    +        console.log(this.value); // enregistre undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Empilement des appels avec setInterval

    +

    setTimeout exécute la fonction une seule fois. setInterval - comme son nom le suggère - exécutera la fonction toutes les 'X' millisecondes, mais son utilisation est découragée.

    +

    Lorsque que du code en cours d'exécution bloque la temporisation, setInterval continuera a émettre plusieurs appels à la fonction spécifiée. Cela peut, en particulier avec un petit intervalle, résulter à un empilement d'appels de fonction.

    +
    function foo(){
    +    // qq chose qui bloque pendant 1 seconde
    +}
    +setInterval(foo, 100);
    +

    Dans le code ci-dessus, foo sera appelé une fois et bloquera pendant une seconde.

    +

    Pendant ce temps, setInterval va continuer à planifier les appels à la fonction. Quand foo se termine, il y aura déjà dix autres appels qui attendent pour s'exécuter.

    +

    Traiter le code bloquant éventuel

    +

    La solution la plus simple et qui offre le plus de contrôle est d'utiliser setTimeout dans la fonction elle-même.

    +
    function foo(){
    +    // qq chose qui bloque pendant 1 seconde
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Non seulement cela encapsule l'appel setTimeout, mais il empêche également l'empilement des appels et donne un contrôle supplémentaire. La fonction foo elle-même peut maintenant décider si elle veut s'exécuter à nouveau ou non.

    +

    Effacer un délais d'attente

    +

    L'effacement des délais d'attente et des intervalles fonctionne en transmettant l'identifiant retourné par la fonction setTimeout ou setInterval à clearTimeout ou clearInterval, respectivement.

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

    Effacer tous les délais d'attente

    +

    Comme il n'existe pas de méthode intégrée pour effacer tous les délais d'attente et/ou intervalles, il est nécessaire d'utiliser la force brute pour obtenir cette fonctionnalité.

    +
    // Effacement de "tous" les délais d'attente
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Mais il pourrait encore y avoir des délais d'attente qui ne sont pas effacés par ce nombre arbitraire. +Une autre façon de faire découle du fait que l'identifiant donné à un délai d'attente est incrémenté à chaque fois que vous appelez setTimeout.

    +
    // Effacement de "tous" les délais d'attente
    +var identifiantLePlusGrand = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= identifiantLePlusGrand; i++) {
    +    clearTimeout(i);
    +}
    +

    Même si cela fonctionne sur tous les principaux navigateurs d'aujourd'hui, il ne est pas précisé que les identifiants doivent être ordonnés de cette façon et cela peut changer. Par conséquent, il est plutôt recommandé de garder une trace de tous les identifiant crées, pour qu'ils puissent être effacées spécifiquement.

    +

    Utilisation cachée de eval

    +

    setTimeout et setInterval peuvent également prendre une chaîne de caractères comme premier paramètre. +Cette fonctionnalité ne devrait jamais être utilisée car elle utilise eval en interne.

    + +
    function foo() {
    +    // sera appelé
    +}
    +
    +function bar() {
    +    function foo() {
    +        // ne sera jamais appelé
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Puisque eval n'est pas appelé directement, la chaîne passé à setTimeout sera exécutée dans la portée globale; ainsi, la variable foo locale à bar ne sera pas utilisée.

    +

    Il est aussi recommandé de ne pas utiliser une chaîne pour passer des arguments à la fonction qui sera appelée par l'une des fonctions de temporisation.

    +
    function foo(a, b, c) {}
    +
    +// ne JAMAIS faire cela
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// utiliser plutôt une fonction anonyme
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    En conclusion

    +

    Une chaîne ne devrait jamais être passée comme paramètre de setTimeout ou setInterval pour passer des arguments à la fonction appelée. C'est un clair signe de mauvais code. Il faut appeler une fonction anonyme qui se charge d'appeler la fonction réelle avec les arguments nécessaires.

    +

    En outre, l'utilisation de setInterval doit être évitée car son programmateur n'est pas bloqué par le code en train de s'exécuter.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/hu/index.html b/external/JavaScript-Garden/hu/index.html new file mode 100644 index 0000000..9ac0a3d --- /dev/null +++ b/external/JavaScript-Garden/hu/index.html @@ -0,0 +1,1451 @@ +JavaScript Garden

    Bevezető

    Bevezető

    A JavaScript Garden egy folytonosan növekvő kódgyűjtemény amely a nyelv kihívást okozó részeit hivatott tisztázni. Itt tanácsokat kaphatsz afelől, hogyan kerüld el a leggyakoribb programozási- valamint nyelvi hibákat, hogyan írj gyorsabb kódot, és mik a legsűrűbben elkövetett bad practicek amelyekkel egy átlagos JavaScript programozó (nem) mindennapi útján találkozhat a nyelv megismerése közben.

    +

    A JavaScript Gardennek nem célja, hogy megtanítsa a JavaScript nyelvet! +Az itt felsorolt témák megértéséhez mindenképp szükséges némi tapasztalat. Ha a nyelv alapjait szeretnéd elsajátítani, először nézd át ezt a kiváló tutorialt a Mozilla Developer Networkön.

    +

    Szerzők

    +

    Ez a leírás két kiváló Stack Overflow felhasználó, Ivo Wetzel (szerző) és Zhang Yi Jiang (Design) tollából született.

    +

    A dokumentumot jelenleg Tim Ruffles gondozza.

    +

    Hozzájárultak még

    + +

    Hosting

    +

    A JavaScript Garden a GitHubon van hostolva, de a Cramer Development jóvoltából +a JavaScriptGarden.info címen is elérhető.

    +

    Licensz

    +

    A JavaScript Garden az MIT licensszel van ellátva és a GitHubon hostoljuk. +Ha bármilyen hibát vagy elírást veszel észre, kérlek jelezd azt, vagy javítsd és küldj egy pull requestet. Továbbá, megtalálhatsz minket a JavasScript szobában a Stack Overflow chaten.

    +

    Objektumok

    Objektumok és mezők használata

    A JavaScriptben minden objektumként működik, a null és az undefined kivételével.

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

    Gyakori tévhitként terjed, hogy a JavaScriptben a számok nem használhatóak objektumként. +Ez csak látszólag igaz, mivel a JavaScript a pont utáni részt úgy próbálja értelmezni, +mintha lebegőpontos számot látna. Így hibát kaphatunk.

    +
    2.toString(); // SyntaxErrort vált ki
    +

    Azonban számos kifejezés létezik megoldásként, amelyekkel megkerülhető ez a probléma.

    +
    2..toString(); // így a második pont már az objektumra utal
    +2 .toString(); // fontos a space-t észrevenni itt a pont előtt
    +(2).toString(); // a 2 értékelődik ki hamarabb
    +

    Objektumok mint adattípusok

    +

    Az objektumok JavaScriptben Hash táblaként is használhatóak, mivel természetszerűleg kulcs-érték párokat tartalmaznak.

    +

    Az objektum literál leírásával - {} jelöléssel - lehet létrehozni egy új objektumot. Ez az új objektum az Object.prototype-ból származik és nincsenek saját mezői definiálva.

    +
    var foo = {}; // egy új, üres objektum
    +
    +// egy új objektum egy 'test' nevű mezővel, aminek 12 az értéke
    +var bar = {test: 12}; 
    +

    Mezők elérése

    +

    Egy objektum mezői kétféle módon érhetőek el, vagy az 'objektum.mezőnév' jelöléssel, +(Ford.: amit "dot notationként" emlegetünk) vagy a szögletes zárójelek kirakásával.

    +
    var foo = {name: 'macska'}
    +foo.name; // macska
    +foo['name']; // macska
    +
    +var get = 'name';
    +foo[get]; // macska
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // működik
    +

    A két jelölés majdnem egyenértékűen használható, kivéve, hogy a szögletes zárójelekkel dinamikusan állíthatunk be mezőket és olyan neveket is választhatunk, amik amúgy szintaxis hibához vezetnének (Fordító: mivel a neveket stringbe kell rakni, megadhatunk a JS által "lefoglalt" kulcsszavakat is mezőnévként, habár ennek használata erősen kerülendő).

    +

    Mezők törlése

    +

    Egyetlen módon lehet mezőt törölni egy objektumból, ez pedig a delete operátor +használata; a mező értékének undefined-ra vagy null-ra való állítása csak +magára az értékre van kihatással, de a kulcs ugyanúgy megmarad az objektumban.

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

    A fenti ciklus a bar undefined és a foo null eredményeket fogja kiírni - +egyedül a baz mező került törlésre, és emiatt hiányzik is az outputról.

    +

    Kulcsok jelölése

    +
    var test = {
    +    'case': 'Kulcsszó vagyok, ezért stringként kell leírnod',
    +    delete: 'Én is az vagyok' // SyntaxError
    +};
    +

    Az objektumok mezőnevei mind stringként, mind egyszerű szövegként (Ford.: aposztrófok nélkül) +leírhatóak. A JavaScript értelmező hibája miatt, a fenti kód azonban SyntaxErrort eredményez ECMAScript 5 előtti verzió esetén.

    +

    Ez a hiba onnan ered, hogy a delete egy kulcsszó, viszont érdemes string literálként +leírni, hogy helyesen megértsék a régebbi JavaScript motorok is.

    +

    A Prototípus

    A JavaScript nem a klasszikus öröklődést használja, hanem egy ún. prototípusos +származtatást használ.

    +

    Míg ezt gyakran a JavaScript legnagyobb hibái között tartják számon, valójában +ez a származtatási modell jóval kifejezőbb, mint klasszikus barátja. +Ezt jelzi, hogy például sokkal könnyebb megépíteni a klasszikus modellt, alapul véve +a prototípusos modellt, míg a fordított irány kivitelezése igencsak nehézkes lenne.

    +

    A JavaScript az egyetlen széles körben elterjedt nyelv, amely ezt a származtatást +használja, így mindenképp időt kell szánni a két modell közti különbség megértésére.

    +

    Az első feltűnő különbség, hogy ez a fajta származtatás prototípus láncokat +használ.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Beállítjuk a Bar prototípusát a Foo egy új példányára
    +Bar.prototype = new Foo(); // !
    +Bar.prototype.foo = 'Hello World';
    +
    +// Beállítjuk a Bar konstruktorát
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // új Bar példány létrehozása
    +
    +// A kapott prototípus lánc
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* stb. */ }
    +

    A fenti kódban a test objektum mind a Bar.prototype és Foo.prototype +prototípusokból származik, így lesz hozzáférése a method nevű függvényhez amely +a Foo prototípusában lett definiálva. A value mezőhöz szintén lesz hozzáférése, +amely akkor jött létre, amikor (szám szerint) egy új Foo példányt hoztunk létre. +Érdemes észrevenni hogy a new Bar() kifejezés nem hoz létre egy új Foo példányt +minden alkalommal, azonban újrahasználja azt az egyetlen (//!) inicilalizált Foo pédlányunkat. Így az összes Bar példány egy és ugyanazt a value mezőt (és +értéket) fogja használni.

    + +

    Mezők keresése

    +

    Amikor olyan utasítást adunk ki, amellyel egy objektum mezőjét keressük, a +JavaScript felfele bejárja az egész prototípus láncot, amíg meg nem találja +a kért mezőt.

    +

    Hogyha eléri a lánc legtetejét - nevezetesen az Object.prototype-t és még +ekkor sem találja a kért mezőt, akkor az undefined-dal fog +visszatérni.

    +

    A Prototype mező

    +

    Alapjáraton, a JavaScript a prototype nevű mezőt használja a prototípus láncok +kialakításához, de ettől függetlenül ez is ugyanolyan mező mint a többi, és +bármilyen értéket be lehet neki állítani. Viszont a primitív típusokat egyszerűen +figyelmen kívül fogja hagyni a feldolgozó.

    +
    function Foo() {}
    +Foo.prototype = 1; // nincs hatása
    +

    Az objektumok megadása, mint azt a fentebbi példában láthattuk, hatással van a prototype +mezőkre és ezeknek az átállításával bele lehet szólni a prototípus láncok kialakításába.

    +

    Teljesítmény

    +

    Értelemszerűen, minél nagyobb a prototípus lánc, annál tovább tart egy-egy mező +felkeresése, és ez rossz hatással lehet a kód teljesítményére. Emellett, ha egy +olyan mezőt próbálunk elérni, amely nincs az adott objektum példányban, az mindig +a teljes lánc bejárását fogja eredményezni.

    +

    Vigyázat! Akkor is bejárjuk a teljes láncot, amikor egy objektum mezőin próbálunk iterálni.

    +

    Natív prototípusok bővítése

    +

    Egy gyakran elkövetett hiba, hogy az Object.prototype prototípust vagy egy másik előre +definiált prototípust próbálunk kiegészíteni új kóddal.

    +

    Ezt monkey patching-nek is hívják, és aktívan kerülendő, mivel megtöri +az egységbe zárás elvét. Habár ezt a technikát olyan népszerű framework-ök +is használják, mint a Prototype, ettől függetlenül ne hagyjuk magunkat csőbe húzni; +nincs ésszerű indok arra, hogy összezavarjuk a beépített típusokat, további +nem standard saját funkcionalitással.

    +

    Az egyetlen ésszerű használati indok a natív prototípusokba nyúlásra az lehet, +hogy megpróbáljuk szimulálni az új JavaScript motorok szolgáltatásait régebbi társaikon, például az Array.forEach implementálásával.

    +

    Zárásként

    +

    Nagyon fontos megérteni a prototípusos származtatási modellt, mielőtt olyan +kódot próbálnánk írni, amely megpróbálja kihasználni a sajátosságait. Nagyon +oda kell figyelni a prototípuslánc hosszára - osszuk fel több kis láncra ha +szükséges - hogy elkerüljük a teljesítmény problémákat. Továbbá, a natív +prototípusokat soha ne egészítsük ki, egészen addig amíg nem akarunk +JavaScript motorok közötti kompatibilitási problémákat áthidalni.

    +

    hasOwnProperty

    Hogy meg tudjuk nézni egy adott objektum saját mezőit - azokat a mezőket, amelyek +az objektumon közvetlenül vannak definiálva, és nem valahol a +prototípus láncon -, a hasOwnProperty függvény használata +ajánlott, amelyet az összes objektum amúgy is örököl az Object.prototype-ból.

    + +

    A hasOwnProperty függvény az egyetlen olyan dolog amelyik anélkül tudja ellenőrizni +az objektum mezőit, hogy megpróbálná bejárni a prototípus láncot.

    +
    // Az Object.prototype beszennyezése
    +Object.prototype.bar = 1;
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // igaz
    +
    +foo.hasOwnProperty('bar'); // hamis
    +foo.hasOwnProperty('goo'); // igaz
    +

    Hogy megértsük a fontosságát, egyedül a hasOwnProperty tudja hozni a korrekt +és elvárt eredményeket mezőellenőrzés szempontjából. Egyszerűen nincs más +módja annak, hogy kizárjuk a szűrésünkből azokat a mezőket amelyek nem az objektumon, +hanem valahol feljebb, a prototípus láncon lettek definiálva.

    +

    A hasOwnProperty mint mező

    +

    A JavaScript persze nem védi magát a hasOwnProperty nevet, így egy jókedvű +programozóban mindig megvan a lehetőség, hogy így nevezze el a saját függvényét. +Ennek kikerülése érdekében ajánlott mindig a hasOwnProperty-re kívülről hivatkozni +(Értsd: A hackelt -saját hasOwnPropertyvel ellátott- objektum kontextusán kívüli objektum hasOwnPropertyjét hívjuk meg).

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Mordor itt kezdődik'
    +};
    +
    +foo.hasOwnProperty('bar'); // mindig hamissal tér vissza
    +
    +// Használhatjuk egy másik objektum hasOwnPropertyjét, 
    +// hogy meghívjuk a foo-n.
    +({}).hasOwnProperty.call(foo, 'bar'); // ez már igaz
    +
    +// Szintén jó megoldás lehet közvetlenül az 
    +// Object prototypejából hívni ezt a függvényt.
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // ez is igaz
    +

    Konklúzió

    +

    A hasOwnProperty használata az egyetlen megbízható módszer annak eldöntésére, +hogy egy mező közvetlenül az objektumon lett-e létrehozva. Melegen ajánlott a +hasOwnProperty-t minden for in ciklusban használni. +Használatával ugyanis elkerülhetjük a kontár módon kiegészített natív prototípusokból +fakadó esetleges hibákat, amire az imént láttunk példát.

    +

    A for in ciklus

    Csak úgy, mint a jó öreg in operátor, a for in is bejárja az egész +prototípus láncot, amikor egy objektum mezőin próbálnánk iterálni.

    + +
    // Mérgezzük Object.prototype-ot!
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // mind a moo és bar is kiírásra kerül
    +}
    +

    Mivel -hála égnek- magának a for in ciklusnak a működését nem lehet befolyásolni, +így más módszert kell találnunk ahhoz hogy száműzzük a váratlan mezőket a ciklus magból. +(Értsd: Azokat amelyek a prototípus láncon csücsülnek csak). Ezt pedig az Object.prototype-ban +lakó hasOwnProperty függvény használatával érhetjük el.

    + +

    Szűrés használata a hasOwnProperty-vel

    +
    // még mindig a fenti foo-nál tartunk
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Ez az egyetlen helyes útja annak, hogy az objektum saját mezőin iteráljunk csak végig. +Mivel a hasOwnProperty-t használjuk, így csak a várt moo-t fogja kiírni. Tehén jó +kódunk van! Hogyha a hasOwnProperty-t kihagynánk, a kódunk ki lenne téve nem várt +hibáknak, amik pl. abból fakadnak, hogy valaki ocsmányul kiterjesztette az +Object.prototype-t.

    +

    Például, ha a Prototype frameworköt használjuk, és nem ilyen stílusban írjuk a +ciklusainkat, a hibák szinte garantáltak, ugyanis ők saját szájízükre kiterjesztik az +Object.prototype-t.

    +

    Konklúzió

    +

    A hasOwnProperty használata erősen javasolt. Soha ne éljünk pozitív +feltételezésekkel a futó kódot illetően, főleg olyan döntésekben nem érdemes +orosz rulettezni, mint hogy kiterjeszti-e valaki a natív prototípusokat vagy nem. +Mert általában igen.

    +

    Függvények

    Függvény deklarációk és kifejezések

    A függvények JavaScriptben egyben objektumok is. Ez azt jelenti, hogy +ugyanúgy lehet őket passzolgatni, mint bármelyik más értékeket. Ezt a featuret +gyakran használják arra, hogy egy névtelen (callback) függvényt átadjunk +egy másik -aszinkron- függvény paramétereként.

    +

    A függvény deklaráció

    +
    function foo() {}
    +

    Ez a függvény felkerül a scope tetejére (hoisting), mielőtt a kód végrehajtása megtörténne. Így abban a scope-ban, ahol definiálták, mindenhol elérhető, +még abban a trükkös esetben is, hogyha a kód azon pontján hívjuk ezt a függvényt, mielőtt +definiáltuk volna (látszólag).

    +
    foo(); // Így is működik
    +function foo() {}
    +

    A függvény kifejezés (expression)

    +
    var foo = function() {};
    +

    A fentebbi példában egy névtelen függvényt adunk értékül a foo változónak.

    +
    foo; // 'undefined'
    +foo(); // TypeError hiba
    +var foo = function() {};
    +

    Habár ebben a példában a var deklaráció futás előtt a kód tetejére kúszik, +ettől függetlenül a foo mint függvény meghívásakor hibát fogunk kapni.

    +

    Ugyanis a deklaráció felkúszott, azonban az értékadás csak futásidőben fog megtörténni, +addig is a foo változó értéke undefined marad. Az undefinedot pedig hiába hívjuk függvényként, TypeErrort kapunk végeredményül.

    +

    Névvel ellátott függvény kifejezés

    +

    Egy másik érdekes eset, amikor névvel ellátott függvényeket adunk értékül változóknak.

    +
    var foo = function bar() {
    +    bar(); // Működik
    +}
    +bar(); // ReferenceError
    +

    Ebben a példában a bart önmagában nem lehet elérni egy külső scope-ból (utolsó sor), +mivel egyből értékül adtuk a foo változónak. Ennek ellenére a baron belül elérhető +a bar név. A tanulság az, hogy a függvény önmagát mindig eléri a saját scopeján belül, és ez a JavaScriptben található névfeloldásnak köszönhető.

    +

    A this mágikus működése

    A this kicsit másképp működik a JavaScriptben, mint ahogy azt megszokhattuk +más nyelvekben. Ugyanis pontosan öt féle módja lehet annak, hogy a this +éppen mire utal a nyelvben.

    +

    A Globális hatókör

    +
    this;
    +

    Amikor globális hatókörben van használva a this, akkor pontosan a globális objektumra utal.

    +

    Függvény híváskor

    +
    foo();
    +

    Itt a this megint a globális objektumra fog utalni.

    + +

    Eljárás hívásakor

    +
    test.foo(); 
    +

    Ebben a példában a this a test objektumra fog hivatkozni.

    +

    Konstuktor hívásakor

    +
    new foo(); 
    +

    Ha a függvény hívását a new kulcsszóval előzzük meg, akkor a függvény konstruktorként fog viselkedni. A függvényen belül, a this +az újonnan létrehozott Objektumra fog hivatkozni.

    +

    A this explicit beállítása

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // ugyanaz mint egy sorral lejjebb
    +foo.call(bar, 1, 2, 3); // argumentumok: a = 1, b = 2, c = 3
    +

    A Function.prototype-ban levő call vagy apply használatakor aztán elszabadul a pokol 😀. +Ezekben az esetekben ugyanis a this a foo hívásakor egzaktan be lesz állítva az apply/call +első argumentumára.

    +

    Ennek eredményképp az előzőekben említett Eljárás hívásakor rész nem érvényes, +a foo fentebbi meghívásakor a this értéke a bar objektumra lesz beállítva.

    + +

    Gyakori buktatók

    +

    Míg a fent megtalálható eseteknek van gyakorlatban vett értelme, az első +a nyelv rossz designjára utal, ugyanis ennek soha nem lesz semmilyen +praktikus felhasználási módja.

    +
    Foo.method = function() {
    +    function test() {
    +        // A this itt a globális ojjektum.
    +    }
    +    test();
    +};
    +

    Gyakori hiba, hogy úgy gondolják a fenti példában az emberek, hogy a this a test függvényen +belül az őt körülvevő Foo-ra fog mutatni, pedig nem.

    +

    Megoldásképp, hogy a Foo-hoz hozzáférhessük a test-en belül, szükségszerű egy változót +lokálisan elhelyezni a method-on belül, ami már valóban a kívánt this-re (Foo-ra) mutat.

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Használjuk a that-et a this helyett
    +    }
    +    test();
    +};
    +

    A that tuladjonképpen egy mezei változónév (nem kulcsszó), de sokszor használják arra, +hogy egy másik this-re hivatkozzanak vele. A closure-ökkel kombinálva +ez a módszer arra is használható, hogy this-eket passzolgassunk a vakvilágban és még tovább.

    +

    Eljárások értékül adása

    +

    Egy másik koncepció ami nem fog a JavaScriptben működni, az az alias függvények létrehozása, ami tulajdonképpen egy függvény másik névhez való kötését jelentené.

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

    Az első eset miatt a test egy sima függvényhívásként működik, azonban a this értéke +a függvényen belül a továbbiakban nem a someObject lesz.

    +

    Elsőre a this alábbi módon való utánkötése (late binding) nem tűnik jó ötletnek. +Azonban ez az, amitől a prototípusos öröklődés is működni tud, +ami a nyelv egyik fő erőssége.

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

    Amikor a method meghívódik a Bar példányaként, a this pontosan a Bar +megfelelő példányára fog mutatni.

    +

    Closure-ök és referenciák

    A JavaScript nyelv egyik legerőteljesebb tulajdonsága a closure-ök használatában rejlik. +Ezek használatával a hatókörök egymásba ágyazhatóak, és egy belső hatókör mindig hozzáfér +az őt körülvevő, külső hatókör változóihoz. Miután JavaScriptben egyetlen dologgal lehet +hatóköröket kifejezni, és ez a függvény (bizony az if, try/catch és hasonló blokkok nem jelentenek új hatókört, mint pl. a Javaban), az összes funkció closure-ként szerepel.

    +

    Privát változók emulálása

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

    Ebben a példában a Counter két closure-rel tér vissza: az increment és +a get függvénnyel. Mind a két függvény referenciát tárol a Counter hatókörre, +és így mindketten hozzáférnek a count változóhoz, ami ebben a hatókörben lett +definiálva.

    +

    Miért működnek a privát változók?

    +

    Mivel a JavaScriptben egyszerűen nem lehet hatókörre referálni, vagy hatókört +értékül adni, így ezért szintén lehetetlen elérni az iménti count változót a külvilág számára. +Egyetlen mód van a megszólítására, ezt pedig láttuk a fentebbi két closure-ön belül.

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

    A fentebbi kód nem fogja megváltoztatni a Counter hatókör count változóját, +mivel a foo.hack mező nem abban a hatókörben lett létrehozva. Ehelyett, okosan, +létre fogja hozni, vagy felül fogja írni a globális count változót (window.count).

    +

    Closure-ök használata ciklusokban

    +

    Az egyik leggyakoribb hiba, amit el lehet követni, az a closure-ök ciklusokban való használata. +Annak is azon speciális esete amikor a ciklus indexváltozóját szeretnénk lemásolni a closure-ön belül.

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

    A fenti kódrészlet marhára nem a számokat fogja kiírni 0-tól 9-ig, de inkább +a 10-et fogja tízszer kiírni.

    +

    Ugyanis a belső névtelen függvény egy referenciát fog tárolni a külső i változóra, és +akkor, amikor végül a console.log sor lefut, a for loop már végzett az egész ciklussal, +így az i értéke 10-re lesz beállítva.

    +

    Ahhoz, hogy a várt működést kapjuk (tehát a számokat 0-tól 9-ig), szükségszerű az i változó +értékét lemásolni.

    +

    A referencia probléma elkerülése

    +

    Az előző problémára megoldást úgy lehet jól adni, hogy az utasításoknak megfelelően +lemásoljuk a ciklusváltozót, úgy hogy a jelenlegi ciklusmagöt körbevesszük egy névtelen +függvénnyel.

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

    A külső (wrapper) névtelen függvény így azonnal meghívódik az i ciklusváltozóval, mint paraméterrel, +és így mindig egy másolatot fog kapni az i változó értékéről, amit ő e néven emészt tovább.

    +

    Így a setTimeoutban lévő névtelen függvény mindig az e nevű referenciára fog mutatni, aminek az értéke így már nem változik meg a ciklus futása során.

    +

    Egy másik lehetséges út a megoldáshoz az, hogy egy wrapper függvényt visszatérítünk a setTimeoutból, aminek ugyanaz lesz a hatása, mint a fentebbi példának.

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

    Az arguments objektum

    Minden függvényhatókörben hozzáférhető az arguments nevű speciális változó, +amely azon argumentumok listáját tartalmazza, amelyekkel a függvényt meghívták.

    + +

    Lehet hogy úgy néz ki, de az arguments objektum nem egy tömb. Látszólag hasonlít rá, +mivel van például egy length nevű mezője, de igazából nem az Array.prototype-ból "származik", +hanem tisztán az Object-ből.

    +

    Itt jön a trükk lényege, hogy ennek köszönhetően nem használhatóak rajta a standard +tömb műveletek mint például a push, pop vagy a slice. Míg a sima for ciklusos iterálás +működik itt is, ahhoz hogy az előbb említett műveleteket is tudjuk rajta használni, át kell +konvertálni egy valódi Array objektummá.

    +

    Tömbbé konvertálás

    +

    Ez a kódrészlet egy új Array objektummá varázsolja az emlegetett arguments szamarat.

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

    De, ez a konverzió meglehetősen lassú, így egyáltalán nem ajánlott teljesítmény kirtikus +alkalmazások írásakor.

    +

    Argumentumok kezelése

    +

    A következő módszer ajánlott arra az esetre, hogy ha az egyik függvény paramétereit egy-az-egyben +át szeretnénk adni egy másik függvény számára.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // sok okos kód ide
    +}
    +

    Egy másik trükk arra, hogy teljesen független wrapper függvényeket gyártsunk, a call +és apply együttes használata.

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Elkészíti a "method" (this) független verzióját
    +// Ezeket kapja paraméterül: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Eredmény: Foo.prototype.method.call(this, arg1, ...argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    Paraméterek és argumentum indexek

    +

    A háttérben az arguments objektum minden egyes indexére (elemére) egy getter és egy setter +függvényt is kap, csak úgy ahogy a függvény paramétereit is felül tudjuk írni, illetve eltudjuk érni.

    +

    Ennek eredményeképp, az arguments objektumon véghezvitt változtatások szinkronban +változtatják a függvény névvel ellátott paramétereit is.

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

    Teljesítmény mítoszok és trükkök

    +

    Ahogy már azt korábban körvonalaztuk, az arguments objektum csak akkor nem jön létre, +hogyha a függvényhatókörön belül definiálunk egy változót ezzel a névvel, vagy a függvényünk +egyik paraméterének ezt a nevet választjuk.

    +

    Azonban a getterek és setterek mindig létrejönnek, de ez ne zavarjon meg minket, mert +semmiféle befolyása nincs a teljesítményre, pláne olyan kódban ahol sokkal több mindennel +is foglalkozunk, mint az arguments objetkumhoz való hozzáférés.

    + +

    Habár, egyetlen eset van, amelynek komoly hatása lehet a kód teljesítményére a modern +JavaScript motorokban. Ez pedig az arguments.callee használata.

    +
    function foo() {
    +    // ..csinálunk valamit
    +    arguments.callee; // ezzel a függvény objektummal
    +    arguments.callee.caller; // és ennek a hívójával..
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Így viszont nem lehet behelyettesíteni ide...
    +    }
    +}
    +

    A fenti kódban a foo helyére nem lehet egyszerűen behelyettesíteni a függvény törzsét, +mivel a függvény törzsének fogalma kell legyen mind magáról, mind az ő hívójáról. Ez nem csak +hogy azt akadályozza meg, hogy a behelyettesítéssel nyerjünk egy kis többlet performanciát, +de az egységbe zárás elvét is erősen keresztbevágja, hiszen a függvény így erősen támaszkodni +fog a hívó környezetére (kontextusára).

    +

    Emiatt is, az arguments.callee, vagy bármely mezőjének használata erősen kerülendő.

    + +

    Konstruktorok

    Csak úgy, mint minden más, a konstruktorok működése szintén különbözik +a megszokottól. Itt minden függvényhívás amelyet a new kulcsszó előz meg, +konstruktor hívásnak számít.

    +

    A this értéke a konstruktoron - hívott függvényen - belül az újonnan létrehozott objektumra +mutat. Az új objektum prototípusa a konstruktor függvény prototípusával fog megegyezni.

    +

    Ha a konstruktor függvényben nincs return utasítás, akkor automatikusan a this értékével tér vissza - a létrehozott objektummal.

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

    A fenti kódban a Foo függvényt, mint konstruktort hívjuk meg, ami a test változóban +egy új objektumot fog eredményezni. Ennek az objektumnak a prototípusa a Foo prototípusa lesz.

    +

    Trükkös ugyan, de ha mégis van return utasítás az éppen konstruált függvényben, akkor +a függvény hívása az annak megfelelő értékkel fog visszatérni, de csak akkor, ha a +visszatérített érték Objektum típusú.

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // ez egy új üres objektum lesz: {}, a 2 helyett
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // ez a { foo: 1 } objektumot fogja eredményezni
    +

    Hogyha kihagyjuk a new kulcsszó használatát, a függvény nem egy új objektummal fog visszatérni.

    +
    function Foo() {
    +    this.bla = 1; // ez a globális objektumon állít
    +}
    +Foo(); // undefined
    +

    A this JavaScript beli működésének köszönhetően, még ha le is +fut az előbbi kód, akkor a this helyére a globális objektumot képzeljük.

    +

    Gyárak (Factory-k)

    +

    Ahhoz, hogy teljesen el tudjuk hagyni a new kulcsszó használatát, a konstruktor +függvény explicit értékkel kell visszatérjen.

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

    Mindkét Bar-ra történő hívásmód ugyanazt fogja eredményezni. Kapunk általuk +egy újonnan létrehozott objektumot, amelynek lesz egy method nevű mezője, +ami egyébiránt egy Closure.

    +

    Azt is érdekes itt megjegyezni, hogy a new Bar() hívás nem befolyásolja a +visszatérített objektum prototípusát. Mivel a prototípus csak az újonnan +létrehozott objektumon létezik, amit a Bar nem térít vissza (mivel egy explicit +értéket ad vissza).

    +

    A fenti példában nincs funkcionális különbség aközött hogy kiírjuk-e a new +varázsszót avagy nem.

    +

    Új objektumok létrehozása gyárakon keresztül

    +

    Gyakran bevett módszer egy projetkben, hogy a new varázsszó használatát +teljesen elhagyjuk, mert a kiírásának elfelejtése bugokhoz vezetne.

    +

    Ennek érdekében egy új objektum létrehozásához inkább egy gyárat kell +implementálni, és annak a belsejében létrehozni az új objektumot.

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

    A fenti kód ugyan ellenálló a hiányzó new kulcsszó hibáját illetően és +megfelelően használ privát változókat, érdemes +megemlíteni a dolgok kontra részét is.

    +
      +
    1. Több memóriát használ, mivel az így létrehozott objektumok nem +osztják meg a prototípusukat egymás között.
    2. +
    3. A származtatás macerás, mivel a gyár kénytelen ilyenkor lemásolni +az összes származtatandó metódust egy másik objektumról, vagy ezt az objektumot +be kell állítsa a létrehozott új objektum prototípusának.
    4. +
    5. Az a megközelítés miszerint egy kifelejtett new kulcsszó miatt eldobjuk +az objektum teljes prototípusát, ellenkezik a nyelv szellemiségével.
    6. +
    +

    Összefoglaló

    +

    A new varázsszó kihagyása ugyan bugokhoz vezethet, de ez nem megfelelő indok +arra hogy ezért eldobjuk a prototípusok használatát. Végeredményben mindig +az fog dönteni a különböző stílusok megválasztása között, hogy mire van +szüksége éppen az aktuális programunknak. Egy dolog azért elengedhetetlenül +fontos, ez pedig hogy megválasszuk melyik stílust fogjuk használni objektumok +létrehozásra, és ezt konzisztensen használjuk a teljes megoldáson keresztül.

    +

    Névterek és hatókörök

    Habár látszólag a kapcsos zárójelek jelentik a blokkok határait JavaScriptben, +fontos megjegyezni hogy nincsen blokk szintű hatókör, csakis függvény hatókörök +léteznek.

    +
    function test() { // ez egy hatókör
    +    for(var i = 0; i < 10; i++) { // ez meg nem
    +        // utasítások...
    +    }
    +    console.log(i); // 10
    +}
    + +

    A nyelvben nincsenek beépített névterek, ami azt jelenti hogy minden, egyetlen +globálisan megosztott névtérben kerül deklarálásra.

    +

    Akárhányszor egy változóra hivatkozunk, a JavaScript elkezdi felfele utazva +megkeresni hatókörökön, amíg csak meg nem találja. Hogyha elérjük +a globális hatókört és még mindig nem találjuk a keresett változót, akkor egy +ReferenceError hibával gazdagodik a futásidőnk.

    +

    A globális változók csapása

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

    Érdemes észrevenni, hogy a fenti két scriptnek nem ugyanaz a hatása. Az A script +egy foo nevű változót vezet be a globális hatókörben, a B script pedig egy foo +nevű változót deklarál az ő hatókörében.

    +

    Mégegyszer tehát, ez a kettő nem ugyanazt jelenti: a var elhagyásának jópár +beláthatatlan következménye is lehet.

    +
    // globális hatókör
    +var foo = 42;
    +function test() {
    +    // lokális hatókör
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    Itt, a var elhagyása azt eredményezi, hogy a test függvény mindig felülírja +a globális hatókörben definiált foo változó értékét. Habár ez elsőre nem tűnik +nagy dolognak, ha a varokat több száz sornyi JavaScript kódból hagyjuk el, az +olyan hibákhoz vezethet, amit még az anyósunknak se kívánnánk.

    +
    // globális hatókör
    +var items = [/* random lista */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // a subLoop hatóköre
    +    for(i = 0; i < 10; i++) { // hiányzik a var
    +        // elképesztő dolgokat művelünk itt
    +    }
    +}
    +

    Ennél a kódnál a külső ciklus az első subLoop hívás után megáll, mivel a subLoop +felülírja az i változó globális értékét. Hogyha a második for ciklusban használtuk +volna var-t azzal könnyen elkerülhettük volna ezt a hibát. Sose hagyjuk el a var utasítást, ha csak nem direkt az a kívánt hatás, hogy befolyásoljuk a +külső hatókört.

    +

    Lokális változók

    +

    Kétféleképp (és nem több módon) lehet lokális változókat JavaScriptben leírni; ez vagy a függvény paraméter vagy a var utasítás.

    +
    // globális hatókör
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // a test függvény lokális hatóköre
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    Itt a foo és i lokális változók a test hatókörén belül, viszont a baros +értékadás felül fogja írni a hasonló nevű globális változót.

    +

    Hoisting

    +

    A JS hoistolja (megemeli) a deklarációkat. Ez azt jelenti hogy minden var +utasítás és függvény deklaráció az őt körülvevő hatókör tetejére kerül.

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

    A fenti kód átalakul egy másik formára mielőtt lefutna. A JavaScript felmozgatja +a var utasításokat és a függvény deklarációkat, az őket körülvevő legközelebbi +hatókör tetejébe.

    +
    // a var utasítások felkerülnek ide
    +var bar, someValue; // alapból mindegyik 'undefined' értékű lesz
    +
    +// a függvény deklaráció is felkerül ide
    +function test(data) {
    +    var goo, i, e; // ezek is felkerülnek
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // Ez TypeErrorral elszáll, mivel a bar még 'undefined'
    +someValue = 42; // az értékadásokat nem piszkálja a hoisting
    +bar = function() {};
    +
    +test();
    +

    A hiányzó blokk hatókör ténye nem csak azt eredményezi, hogy a var utasítások +kikerülnek a ciklusmagokból, hanem az if utasítások kimenetele is megjósolhatatlan +lesz.

    +

    Habár úgy látszik az eredeti kódban, hogy az if utasítás a goo globális +változót módosítja, a hoisting után látjuk hogy valójában a lokális változóra +lesz befolyással. Trükkös.

    +

    A hoisting tudása nélkül valaki azt hihetné, hogy az alábbi kód egy ReferenceError +-t fog eredményezni.

    +
    // nézzük meg hogy a SomeImportantThing inicializálva lett-e
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Persze ez működik, annak köszönhetően hogy a var utasítás a globális hatókör +tetejére lett mozgatva.

    +
    var SomeImportantThing;
    +
    +// más kódok még inicializálhatják az előbbi változót itt...
    +
    +// ellenőrizzük hogy létezik-e
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Névfeloldási sorrend

    +

    JavaScriptben az összes hatókörnek -beleértve a globálisat is- megvan a maga +this változója, amelyik mindig az aktuális objektumra utal.

    +

    A függvény hatókörökben van még egy speciális arguments +változó is mindig definiálva, amely a függvénynek átadott argumentumokat +tartalmazza.

    +

    Hogy hozzunk egy példát, amikor valaki a foo nevű változót próbálja elérni egy +függvény hatókörön belül, a JavaScript az alábbi sorrendben fogja keresni az adott +változó nevet.

    +
      +
    1. Abban az esetben ha találunk var foo utasítást, használjuk azt.
    2. +
    3. Hogyha bármelyik függvény paraméter neve foo, használjuk azt.
    4. +
    5. Hogyha magának a függvénynek a neve `foo, használjuk azt.
    6. +
    7. Menjünk a külső hatókörre, és kezdjük újra #1-től.
    8. +
    + +

    Névterek

    +

    Hogyha egyetlen globális névterünk van, akkor egy gyakori probléma lehet az, +hogy névütközésekbe futunk. A JavaScriptben szerencsére ez a gond könnyen +elkerülhető a névtelen wrapper függvények használatával.

    +
    (function() {
    +    // egy 'öntartalmazó' névtér
    +
    +    window.foo = function() {
    +        // egy exportált closure
    +    };
    +
    +})(); // a függvényt azonnal végre is hajtjuk
    +

    A névtelen függvények kifejezésekként vannak értelmezve; így +ahhoz hogy meghívhatóak legyenek, először ki kell értékelni őket.

    +
    ( // a függvény kiértékelése a zárójeleken belül
    +function() {}
    +) // a függvény objektum visszatérítése
    +() // az eredmény meghívása
    +

    Persze más kifejezések is használhatóak arra, hogy kiértékeljük és meghívjuk +a függvény kifejezést, amelyek habár szintaxisukban eltérnek, ugyan azt eredményezik.

    +
    // Még több stílus anonymus függvények azonnali hívásához...
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +// és a lista folytatódik...
    +

    Összegzésül

    +

    Az anonym wrapper függvények használata erősen ajánlott a kód egységbezárása +érdekében, saját névtér alkotásához. Ez nem csak hogy megvédi a kódunkat a +névütközésektől, de jobb modularizációhoz is vezet.

    +

    Emelett a globális változók használata nem ajánlott. Bármilyen fajta +használata rosszul megírt kódról árulkodik, amelyik könnyen eltörik és nehezen +karbantartható.

    +

    Tömbök

    Tömb iteráció és tulajdonságok

    Habár a tömbök a JavaScriptben objektumok, nincsen jó ok arra, hogy a for in ciklussal járjuk be őket. +Valójában sokkal több jó ok van arra, hogy miért ne így tegyünk.

    + +

    Mivel a for in ciklus a prototípus láncon levő összes tulajdonságon végigmegy, +és mivel az egyetlen út ennek megkerülésére a hasOwnProperty használata, így majdnem hússzor +lassabb mint egy sima for ciklus.

    +

    Iteráció

    +

    Annak érdekében, hogy a legjobb teljesítményt érjük el a tömbökön való iteráció során, +a legjobb hogyha a klasszikus for ciklust használjuk.

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

    Még egy érdekesség van a fenti példában, ami a tömb hosszának cachelését végzi +a l = list.length kifejezés használatával.

    +

    Habár a length tulajdonság mindig magán a tömbön van definiálva, még mindig +lehet egy kis teljesítmény kiesés amiatt hogy minden iterációban újra meg kell +keresni ezt a tulajdonságot. Persze a legújabb JavaScript motorok talán +használnak erre optimalizációt, de nem lehet biztosan megmondani, hogy ahol a kódunk +futni fog, az egy ilyen motor-e vagy sem.

    +

    Valójában, a cachelés kihagyása azt eredményezheti, hogy a ciklusunk csak +fele olyan gyors lesz mintha a cachelős megoldást választottuk volna.

    +

    A length mező

    +

    Míg a length mező getter függvénye egyszerűen csak visszaadja a tömbben +levő elemek számát, addig a setter függvény használható arra (is), hogy +megcsonkítsuk a tömbünket.

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

    Egy rövidebb hossz alkalmazása csonkítja a tömböt. A nagyobb hossz megadása +értelemszerűen növeli.

    +

    Összegzésül

    +

    A megfelelő teljesítmény érdekében, a for ciklus használata és a length cachelése +ajánlott. A for in ciklus használata a tömbökön a rosszul megírt kód jele, amely +tele lehet hibákkal, és teljesítményben sem jeleskedik.

    +

    Az Array konstruktor

    Mivel az Array konstruktora kétértelműen bánik a paraméterekkel, melegen +ajánlott mindig a tömb literált - [] jelölés - használni új tömbök létrehozásakor.

    +
    [1, 2, 3]; // Eredmény: [1, 2, 3]
    +new Array(1, 2, 3); // Eredmény: [1, 2, 3]
    +
    +[3]; // Eredmény: [3]
    +new Array(3); // Eredmény: []
    +new Array('3') // Eredmény: ['3']
    +

    Abban az esetben, ha ez a konstruktor csak egy szám paramétert kap, akkor +visszatérési értékül egy olyan tömböt fog létrehozni amelynek a length mezője +akkorára van beállítva, ahogy azt megadtuk az argumentumban. Megjegyzendő hogy +csak a length tulajdonság lesz ekkor beállítva; az egyes indexek külön-külön +nem lesznek inicializálva.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // hamis, nincs ilyen index
    +

    A tömb hosszának közvetlen állítása amúgy is csak elég kevés esetben +használható értelmesen, mint például alább, hogyha el akarjuk kerülni a +for ciklus használatát egy string ismétlésekor.

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

    Összegzésül

    +

    Az Array konstruktor közvetlen használata erősen kerülendő. A literálok használata +elfogadott inkább, mivel rövidebbek, tisztább a szintaxisuk és olvashatóbb kódot +eredményeznek.

    +

    Típusok

    Egyenlőség vizsgálat

    A JavaScriptben két különböző megoldás létezik az objektumok egyenlőségének +vizsgálatára

    +

    Az egyenlőség operátor

    +

    Az egyenlőség vizsgálatot végző (egyik) operátort így jelöljük: ==

    +

    A JavaScript egy gyengén típusos nyelv. Ez azt jelenti, hogy az egyenlőség +operátor típuskényszerítést alkalmaz ahhoz, hogy össze tudjon hasonlítani +két értéket.

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

    A fenti táblázat szépen mutatja, hogy mi a típuskényszerítés eredménye, és egyben +azt is, hogy miért rossz szokás a == használata. Szokás szerint, ez megint +olyan fícsör, ami nehezen követhető kódhoz vezethet a komplikált konverziós +szabályai miatt.

    +

    Pláne, hogy a kényszerítés teljesítmény problémákhoz is vezet; ugyanis, mielőtt +egy stringet egy számhoz hasonlítanánk azelőtt a karakterláncot át kell konvertálni +a megfelelő típusra.

    +

    A szigorú(bb) egyenlőség operátor

    +

    Ez az operátor már három egyenlőségjelből áll: ===.

    +

    Ugyanúgy működik, mint az előbbi, kivéve hogy ez a változat nem alkalmaz +típuskényszerítést az operandusai között.

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

    A felső eredmények sokkal egyértelműbbek és ennek köszönhetően sokkal hamarabb +eltörik a kód egy-egy ellenőrzésen. Ettől sokkal hibatűrőbb lesz +a produktumunk, ráadásul teljesítménybeli gondjaink sem lesznek.

    +

    Objektumok összehasonlítása

    +

    Habár mind a ==-t és a ===-t is egyenlőség operátornak hívjuk, eltérően +viselkednek, hogy ha legalább az egyik operandusuk egy objektum.

    +
    {} === {};                   // hamis
    +new String('foo') === 'foo'; // hamis
    +new Number(10) === 10;       // hamis
    +var foo = {};
    +foo === foo;                 // igaz
    +

    Ebben az esetben mindkét operátor identitást és nem egyenlőséget +ellenőriz; tehát azt fogják ellenőrizni hogy az operandus két oldalán +ugyanaz az objektum referencia áll-e, mint az is operátor Pythonban +vagy a pointerek összehasonlítása C-ben. (A ford.: Tehát nem azt, hogy a +két oldalon álló objektumnak például ugyanazok-e a mezői, hanem azt hogy ugyanazon +a memóriacímen található-e a két operandus).

    +

    Összegzésül

    +

    Azt érdemes tehát megjegyezni, hogy a szigorú egyenlőség vizsgálatot érdemes +mindig használni. Amikor szeretnék típuskényszerítést alkalmazni, akkor azt +inkább tegyük meg direkt módon, és ne a nyelv komplikált +automatikus szabályaira bízzuk magunkat.

    +

    A typeof vizsgálat

    A typeof operátor (az instanceof-al karöltve) +lehetőség szerint a JavaScript nyelv egyik legnagyobb buktatója, mivel majdnem +teljesen rosszul működik.

    +

    Habár az instanceof-nak korlátozottan még lehet értelme, a typeof operátor +tényleg csak egyetlen praktikus use case-el rendelkezik és ez nem az, hogy egy +objektum típusvizsgálatát elvégezzük.

    + +

    Az instanceof operátor

    Az instanceof operátor a két operandusának konstruktorait hasonlítja össze. +Csak akkor bizonyul hasznosnak, amikor saját készítésű objektumokon alkalmazzuk. +Beépített típusokon ugyanolyan hasztalan alkalmazni, mint a typeof operátort.

    +

    Saját objektumok összehasonlítása

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // igaz
    +new Bar() instanceof Foo; // igaz
    +
    +// Ez csak a Bar.prototypeot beállítja a Foo fv. objektumra,
    +// de nem egy kimondott Foo példányra
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // hamis
    +

    Az instanceof reakciója natív típusokra

    +
    new String('foo') instanceof String; // igaz
    +new String('foo') instanceof Object; // igaz
    +
    +'foo' instanceof String; // hamis
    +'foo' instanceof Object; // hamis
    +

    Érdemes itt megjegyezni hogy az instanceof nem működik olyan objektumokon, +amelyek különböző JavaScript kontextusokból származnak (pl. különböző dokumentumok +a böngészőn belül), mivel a konstruktoruk nem pontosan ugyanaz az objektum lesz.

    +

    Összegzésül

    +

    Az instanceof-ot tehát csak megegyező JS kontextusból származó, saját készítésű objektumoknál használjuk. Minden más felhasználása kerülendő, csak úgy, mint a typeof operátor esetén.

    +

    Típus kasztolás

    Előre kössük le, hogy a JavaScript egy gyengén típusos nyelv, így ahol +csak tud, ott típus kényszerítést használ.

    +
    // Ezek igazak
    +new Number(10) == 10; // A Number.toString() számmá lesz
    +                      // visszaalakítva
    +
    +10 == '10';           // A Stringek visszaalakulnak számmá
    +10 == '+10 ';         // Mégtöbb string varázslat
    +10 == '010';          // és mégtöbb
    +isNaN(null) == false; // a null varázslatosan 0-vá alakul
    +                      // ami persze nem NaN
    +
    +// Ezek hamisak
    +10 == 010;
    +10 == '-10';
    + +

    Hogy elkerüljük a fenti varázslatokat, a szigorú egyenlőség ellenőrzés melegen ajánlott. Habár ezzel elkerüljük +a problémák farkasrészét, még mindig tartogat a JS gyengén típusos rendszere +meglepetéseket.

    +

    Natív típusok konstruktorai

    +

    A jó hír az, hogy a natív típusok, mint a Number és a String különféle +módon viselkednek, hogy ha a new kulcsszóval avagy anélkül vannak inicializálva.

    +
    new Number(10) === 10;     // Hamis, Objektum vs. Szám
    +Number(10) === 10;         // Igaz, Szám vs. szám
    +new Number(10) + 0 === 10; // Igaz, az implicit konverziónak hála
    +

    Ha egy natív típust, mint a Number konstruktorként kezelünk, akkor egy új +Number objektumot kapunk. De ha kihagyjuk a new kulcsszót akkor a Number +egy egyszerű konverter függvényként fog viselkedni.

    +

    Ráadásul a literálok passzolgatásakor még több típuskonverzió üti fel a fejét.

    +

    A legjobb megoldás hogyha a három típus valamelyikére expliciten kasztolunk.

    +

    Stringre kasztolás

    +
    '' + 10 === '10'; // igaz
    +

    Egy üres string hozzáfűzésével könnyen tudunk egy értéket stringgé kasztolni.

    +

    Számra kaszt

    +
    +'10' === 10; // igaz
    +

    Az unáris plusz operátor használatával lehetséges egy értéket számra alakítani.

    +

    Booleanre kasztolás

    +

    A nem operátor kétszeri alkalmazásával tudunk booleanné kasztolni.

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

    Lényeg

    Miért Ne Használjuk az eval-t

    Az eval (evil) funkció egy stringbe ágyazott JavaScript kódot futtat a +lokális scopeon belül.

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

    Viszont az eval csak akkor viselkedik így, hogyha expliciten hívjuk meg +és a meghívott funkció neve valóban eval.

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

    Az eval használata kerülendő. A "felhasználása" az esetek 99.9%-ban +mellőzhető.

    +

    Az eval ezer arca

    +

    A setTimeout és setInterval nevű timeout függvények is +tudnak úgy működni, hogy első paraméterükként egy stringbe ágyazott kódot várnak. +Ez a string mindig a globális hatókörben lesz végrehajtva, mivel az evalt +így nem direktben hívjuk meg.

    +

    Biztonsági problémák

    +

    Az eval azért is veszélyes, mert bármilyen JS kódot végrehajt, amit odaadunk +neki. Éppen ezért sose használjuk olyan kódok végrehajtására amiknek az eredete +nem megbízható/ismeretlen.

    +

    Összegzésül

    +

    Soha ne használjunk evalt. Bármilyen kód működése, teljesítménye, ill. biztonsága +megkérdőjelezhető, amely használja ezt a nyelvi elemet. Semmilyen megoldás +használata nem ajánlott amely első sorban evalra épül. Ekkor egy jobb +megoldás szükségeltetik, amely nem függ az evaltól.

    +

    Az undefined és a null

    A JavaScript két értéket is tartogat a semmi kifejezésére, ezek a null és az +undefined és ezek közül az utóbbi a hasznosabb.

    +

    Az undefined

    +

    Ha az előbbi bevezetőtől nem zavarodtál volna össze; az +undefined egy típus amelynek pontosan egy értéke van, az undefined.

    +

    A nyelvben szintén van egy undefined nevű globális változó amelynek az értékét +hogy-hogy nem undefined-nak hívják. Viszont ez a változó nem konstans vagy +kulcsszó a nyelvben. Ez azt jeletni hogy az értéke könnyedén felülírható.

    + +

    Itt van pár példa, hogy mikor is találkozhatunk az undefined értékkel:

    +
      +
    • Az undefined globális változó elérésekor
    • +
    • Egy deklarált, de nem inicializált változó elérésekor.
    • +
    • Egy függvény hívásakor ez a visszatérési érték, return utasítás híján.
    • +
    • Egy olyan return utasítás lefutásakor, amely nem térít vissza értéket.
    • +
    • Nem létező mezők lekérésekor.
    • +
    • Olyan függvény paraméterek elérésekor amelyeknek a hívó oldalon nem kaptak értéket.
    • +
    • Bármikor amikor az undefined érték van valaminek beállítva.
    • +
    • Bármelyik void(kifejezés) utasítás futtatásakor.
    • +
    +

    undefined megőrzési trükkök

    +

    Mivel az undefined nevű globális változó csak egy másolatot tárol az +undefined elnevezésű értékből, az értékének megváltoztatása nem írja +felül az eredeti undefined típus értékét.

    +

    Ezért, ha valamilyen értékkel össze szeretnénk hasonlítani az undefined értéket, +nem árt hogyha először magát az undefined-ot el tudjuk érni.

    +

    Egy gyakori technika annak érdekében hogy megvédjük a kódunkat az +undefined lehetséges felüldefiniálásaitól, hogy egy névtelen (wrapper) függvénybe +csomagoljuk az egész kódunkat, amelynek lesz egy direkt üres paramétere.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // az undefined ebben a hatókörben 
    +    // megint valóban az `undefined` értékre referáll.
    +
    +})('Hello World', 42);
    +

    Egy másik módja ennek, hogy használunk egy "üres" deklarációt a wrapper függvényen +belül.

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

    Az egyetlen különbség ebben a változatban, hogyha minifikáljuk ezt a kódot, +és nem definiálunk további változókat ezen a részen belül, akkor ezzel a +változattal extra 4 byte "veszteséget" szenvedünk el.

    +

    Mikor használjunk nullt

    +

    Miközben az undefined a natív JavaScript megvalósításokban inkább a (más +nyelvekben levő) tradícionális null helyett használandó, azalatt maga a null +inkább csak egy különböző adattípusnak számít, mindenféle különös jelentés nélkül.

    +

    Egy pár belső JavaScriptes megoldásban ugyan használják (ahol pl. a prototípus lánc végét a Foo.prototype = null beállítással jelölik), de a legtöbb esetben ez +felcserélhető az undefined-al.

    +

    (A ford.: A null annak az esetnek a jelölésére hasznos, amikor +egy referencia típusú változót deklarálunk, de még nem adunk neki értéket. Pl. a +var ezObjektumLesz = null kifejezés ezt jelöli. Tehát a null leginkább +kezdeti értékként állja meg a helyét, minden másra ott az undefined)

    +

    Automatic Semicolon Insertion

    Bár a JavaScriptnek látszólag C-s szintaxisa van, mégsem kötelező benne +kirakni a pontosvesszőket, így (helyenként) kihagyhatóak a forrásból. +(A ford.: hiszen interpretált nyelv lévén nincsenek fordítási hibák, így +nyelvi elemek meglétét sem tudja erőltetni a nyelv)

    +

    Itt jön a csel, hogy ennek ellenére a JavaScript csak pontosvesszőkkel +értelmezi megfelelően a beírt kódot. Következésképp, a JS automatikusan +illeszti be a pontosvesszőket (megpróbálja kitalálni a gondolataink) +azokra a helyekre, ahol amúgy emiatt értelmezési hibába futna.

    +
    var foo = function() {
    +} // értelmezési hiba, pontosvessző kéne
    +test()
    +

    Az automatikus beillesztés megtörténik, ezután így értelmeződik a kód

    +
    var foo = function() {
    +}; // nincs hiba, mindenki örül
    +test()
    +

    Az automatikus beillesztés (ASI) a JavaScript (egyik) legnagyobb design +hibája, mivel igen... meg tudja változtatni a kód értelmezését

    +

    Hogyan Működik

    +

    Az alábi kódban nincsen pontosvessző, így az értelmező (parser) feladata kitalálni, +hogy hova is illessze be őket.

    +
    (function(window, undefined) {
    +    function test(options) {
    +        log('testing!')
    +
    +        (options.list || []).forEach(function(i) {
    +
    +        })
    +
    +        options.value.test(
    +            'hosszú string az argumentumban',
    +            'még még még még még hossszabbbbbbb'
    +        )
    +
    +        return
    +        {
    +            foo: function() {}
    +        }
    +    }
    +    window.test = test
    +
    +})(window)
    +
    +(function(window) {
    +    window.someLibrary = {}
    +
    +})(window)
    +

    Alább mutatjuk a "kitalálós" játék eredményét.

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Nincs beillesztés, a sorok össze lettek vonva
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- beillesztés
    +
    +        options.value.test(
    +            'hosszú string az argumentumban',
    +            'még még még még még hossszabbbbbbb'
    +        ); // <- beillesztés
    +
    +        return; // <- beillesztés, eltörik a return kifejezésünk
    +        { // blokként értelemződik
    +
    +            // név: kifejezés formátumban értelmeződik
    +            foo: function() {} 
    +        }; // <- beillesztés
    +    }
    +    window.test = test; // <- beillesztés
    +
    +// Ezeket a sorokat összeilleszti
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- beillesztés
    +
    +})(window); //<- beillesztés
    + +

    Az értelmező drasztikusan megváltoztatta a fenti kódot. A legtöbb esetben a +beillesztő rosszul tippel.

    +

    (A ford.: Semmilyen nyelvben sem jó, hogyha hagyjuk hogy a gép találja ki mit +szerettünk volna írni. Néma gyereknek az anyja sem érti a kódját ugye.)

    +

    Kezdő Zárójelek

    +

    Az értelmező nem rak be új pontosvesszőt, hogyha a sor eleje (nyitó) zárójellel kezdődik.

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

    Ez a kód egy sorként értelmeződik

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

    Az esélyek arra elég magasak, hogy a log nem egy függvényt fog visszatéríteni; így a fenti kód egy TypeError típusú hibát fog dobni +undefined is not a function üzenettel.

    +

    Összefoglalásképp

    +

    Szükségszerűen soha ne hagyjuk ki a pontoszvesszőket. Nem árt a kapcsos +zárójeleket is ugyanazon a soron tartani, mint amelyiken az utasítást elkezdtük, +így nem ajánlott az egysoros if / else kifejezések kedvéért elhagyni +őket. Ezek a szempontok nem csak a kódot (és annak olvashatóságát) tartják +konzisztensen, de megelőzik azt is hogy a JavaScript értelmező valamit rosszul +"találjon ki".

    +

    A delete Operátor

    Röviden, lehetetlen globális változókat, függvényeket és olyan dolgokat törölni +JavaScriptben amelyeknek a DontDelete attribútuma be van állítva.

    +

    Globális kód és Függvény kód

    +

    Amikor egy változó/függvény, globális vagy +függvény hatókörben van definiálva, +akkor az vagy az Aktivációs (Activation) vagy a Globális (Global) objektum egyik mezőjeként +jön létre. Az ilyen mezőknek van egy halom attribútuma, amelyek közül az egyik +a DontDelete. A változó és függvény deklarációk a globális vagy függvény kódon +belül mindig DontDelete tulajdonságú mezőket hoznak létre, így nem lehet őket +törölni.

    +
    // globális változó
    +var a = 1; // A DontDelete be lett állítva
    +delete a; // hamis
    +a; // 1
    +
    +// függvény:
    +function f() {} // A DontDelete be lett állítva
    +delete f; // hamis
    +typeof f; // "function"
    +
    +// új értékadással sem megy
    +f = 1;
    +delete f; // hamis
    +f; // 1
    +

    Explicit mezők

    +

    Az expliciten beállított mezőket persze normálisan lehet törölni.

    +
    // expliciten beállított mező
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // igaz
    +delete obj.y; // igaz
    +obj.x; // undefined
    +obj.y; // undefined
    +

    A fenti példában az obj.x és obj.y törölhető, mivel nincs DontDelete +attribútuma egyik mezőnek sem. Ezért működik az alábbi példa is.

    +
    // működik, kivéve IE-ben
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // igaz - egy globális változó
    +delete GLOBAL_OBJECT.a; // igaz
    +GLOBAL_OBJECT.a; // undefined
    +

    Itt egy trükköt használunk az a törlésére. A this itt +a Globális objektumra mutat, és expliciten bevezetjük rajta az a változót, mint +egy mezőjét, így törölni is tudjuk.

    +

    Mint az szokás, a fenti kód egy kicsit bugos IE-ben (legalábbis 6-8-ig).

    +

    Függvény argumentumok és beépített dolgaik

    +

    A függvény argumentumok, az arguments objektum +és a beépített mezők szintén DontDelete tulajdonságúak.

    +
    // függvény argumentumok és mezők
    +(function (x) {
    +
    +  delete arguments; // hamis
    +  typeof arguments; // "object"
    +
    +  delete x; // hamis
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // hamis
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    Vendég (host) objektumok

    +

    A delete operátor működése megjósolhatatlan a vendég objektumokra. A specifikáció +szerint ezek az objektumok szükség szerint bármilyen viselkedést implementálhatnak.

    +

    (A ford.: Vendég objektumok azok az objektumok, amelyek nincsenek konkrétan +meghatározva az ES aktuális verziójú specifikációjában, pl. a window)

    +

    Összegzésképp

    +

    A delete működése helyenként megjósolhatatlan, így biztonsággal csak olyan +objektumok mezőin használhatjuk, amelyeket expliciten mi állítottunk be.

    +

    Egyéb

    A varázslatos setTimeout és setInterval

    Mivel a JavaScript aszinkron, a setTimeout és setInterval használatával +lehetséges késleltetni a kódok lefutási idejét.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // Egy számmal (> 0) tér vissza
    +

    Amikor a setTimeout függvényt meghívjuk, válaszul egy timeout ID-t kapunk, +valamint be lesz ütemezve a foo függvényhívás, hogy körülbelül 1000 miliszekundum múlva fusson le a jövőben. A foo egyszer lesz végrehajtva.

    +

    Az aktuális JavaScript motor időzítésétől függően, és annak figyelembe vételével, +hogy a JavaScript mindig egyszálú, tehát a megelőző kódok blokkolhatják a szálat, +soha nem lehet biztonságosan meghatározni hogy valóban a kért időzítéssel +fog lefutni a kód amit megadtunk a setTimeoutban. Erre semmilyen biztosíték nincs.

    +

    Az első helyen bepasszolt függvény a globális objektum által lesz meghívva, ami +azt jelenti hogy a this a függvényen belül a globális objektumra +utal.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // a this egy globális objektumra utal, nem a Foo-ra
    +        console.log(this.value); // undefined-ot logol ki
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Híváshalmozás a setIntervalal

    +

    Míg a setTimeout csak egyszer futtatja le a megadott függvényt, a setInterval - +ahogy a neve is mutatja - minden X miliszekundumban végrehajtja a +neki átadott kódot, használata pedig erősen kerülendő.

    +

    Nagy hátulütője, hogy még akkor is ütemezi az újabb és újabb +hívásokat, hogyha az aktuálisan futattot kód a megadott időintervallumon +felül blokkolja a további kód futtatást. Ez, hogyha megfelelően rövid +intervallumokat állítunk be, felhalmozza a függvényhívásokat a call stacken.

    +
    function foo(){
    +    // kód ami 1 másodpercig feltartja a futtatást
    +}
    +setInterval(foo, 100);
    +

    A fenti kódban amikor a foo meghívódik, 1 másodpercig feltartja a további futtatást.

    +

    A setInterval persze ütemezni fogja a jövőbeli foo hívásokat továbbra is, amíg +blokkolódik a futtatás. Így tíz további hívás fog várakozni, miután a foo +futtatása először végzett.

    +

    Hogyan Bánjunk El a Blokkolással

    +

    A legkönnyebb és kontrollálhatóbb megoldásnak az bizonyul, ha a setTimeout +függvényt a rögtön a foo-n belül használjuk.

    +
    function foo(){
    +    // 1 másodpercig blokkoló kód
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Ez nem csak egységbe zárja a setTimeout hívást, de meggátolja a felesleges hívások +felhalmozását, és több irányítást ad a kezünkbe. A foo így magától el tudja +dönteni, hogy akarja-e újra futtatni önmagát vagy sem.

    +

    Timeout Tisztogatás Kézzel

    +

    A clearTimeout vagy clearInterval hívással tudjuk a timeoutjainkat +megszüntetni, természetesen attól függ, hogy melyiket használjuk, +hogy melyik set függvénnyel indítottuk útjára a timeoutunkat.

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

    Az Összes Timeout Megszüntetése

    +

    Mivel nincsen beépített megoldás az összes timeout és/vagy interval +hívás törlésére, ezért bruteforce módszerekhez kell folyamodjunk.

    +
    // az "összes" timeout kitörlése
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Persze ez csak véletlenszerű lövöldözés, semmi sem garantálja, hogy a fenti +módszerrel nem marad timeout a rendszerben (A ford.: például az ezredik timeout vagy +afelett). Szóval egy másik módszer ennek megoldására, hogy feltételezzük hogy +minden setTimeout hívással az azonosítók száma egyel növekszik.

    +
    // az "összes" timeout kiírtása
    +var legnagyobbTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= legnagyobbTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    Habár ez a megoldás minden böngészőben megy (egyelőre), ez az azonosítókról született mondás nincs specifikációban rögzítve, és ennek megfelelően változhat. +Az ajánlott módszer továbbra is az, hogy kövessük nyomon az összes timeout azonosítót, amit generáltunk, és így ki is tudjuk őket rendesen törölni.

    +

    eval A Színfalak Mögött

    +

    Habár a setTimeout és a setInterval (kód) stringet is tud első paramétereként +fogdani, ezt a fajta formáját használni kimondottan tilos, mivel a függöny +mögött ő is csak evalt használ.

    + +
    function foo() {
    +    // meg lesz hívva
    +}
    +
    +function bar() {
    +    function foo() {
    +        // soha nem hívódik meg
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Mivel az evalt nem direkt módon hívjuk meg a fenti esetben, +a setTimeoutnak passzolt string a globális hatókörben fog lefutni; így +a lokális foo függvényt sosem használjuk a bar hatóköréből.

    +

    Továbbá nem ajánlott argumentumokat átadni annak a függvénynek amelyik +a timeout függvények által meg lesz hívva a későbbiekben.

    +
    function foo(a, b, c) {}
    +
    +// SOHA ne használd így!
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// Ehelyett csomagoljuk névtelen függvénybe
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    Összegzésképp

    +

    Soha ne használjunk stringeket a setTimeout vagy setInterval első +paramétereiként. Ha argumentumokat kell átadni a meghívandó függvénynek, az +egyértelműen rossz kódra utal. Ebben az esetben a függvényhívás +lebonyolításához egy anonim függvény használata ajánlott.

    +

    Továbbá, mivel az ütemező kódja nem blokkolódik a JavaScript futás által, a +setInterval használata úgy általában kerülendő.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/image/sidebar-icon.png b/external/JavaScript-Garden/image/sidebar-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4b0b4dd0eb52135f029f640d6d216fbd5acd3c4b GIT binary patch literal 3351 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 DGHA@% literal 0 HcmV?d00001 diff --git a/external/JavaScript-Garden/index.html b/external/JavaScript-Garden/index.html new file mode 100644 index 0000000..4240f34 --- /dev/null +++ b/external/JavaScript-Garden/index.html @@ -0,0 +1,1545 @@ +JavaScript Garden

    Intro

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

    +

    It's currently maintained by Tim Ruffles.

    +

    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.

    +

    Objects

    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 sayHello(){}
    +sayHello.count = 1;
    +sayHello.count; // 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 that can be used to make number literals act +as objects too.

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

    Objects as a Data Type

    +

    Objects in JavaScript can also be used as Hashmaps; they mainly consist +of named properties mapping to values.

    +

    Using an object literal - {} notation - it is possible to create a +plain object. This new object inherits from Object.prototype and +does not have own properties defined.

    +
    var names = {}; // a new empty object
    +
    +// a new object with a 'name' property with value 'Rob'
    +var rob = {name: 'Rob'}; 
    +

    Accessing Properties

    +

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

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

    The notations work almost identically, with the only difference being that the +square bracket notation allows for dynamic setting of properties and +the use of property names that would otherwise lead to a syntax error.

    +

    Deleting Properties

    +

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

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

    The above outputs both a undefined and b null - only c 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, so me too' // 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 a +prototypal model, while the other way around is a far more difficult task.

    +

    JavaScript is the only widely used language that features prototypal +inheritance, so it can take time to adjust to the differences between the two +models.

    +

    The first major difference is that inheritance in JavaScript uses 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', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    In the code 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.

    +

    If 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. However, +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, and this may be significant in code where +performance is critical. 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 be 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 popular 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 essential to understand the prototypal inheritance model before +writing complex code that makes use of it. Also, be aware of the length of the +prototype chains in your code and break them up if necessary to avoid possible +performance problems. Further, the native prototypes should never be +extended unless it is for the sake of compatibility with newer JavaScript +features.

    +

    hasOwnProperty

    To check whether an 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. See the section +on for in loops for more details on when to use +hasOwnProperty when iterating over object +properties.

    +

    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 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
    +
    +// It's also possible to use hasOwnProperty from the Object
    +// prototype for this purpose
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
    +

    In Conclusion

    +

    Using hasOwnProperty is the only reliable method to check for the +existence of a property on an object. It is recommended that hasOwnProperty +be used in many cases when iterating over object properties as described +in the section on for in loops.

    +

    The for in Loop

    Just like the in operator, the for in loop 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. In +ECMAScript 3 and older, this is done using the hasOwnProperty +method of Object.prototype.

    +

    Since ECMAScript 5, Object.defineProperty can be used with +enumerable set to false to add properties to objects (including Object) +without these properties being enumerated. In this case it is reasonable +to assume in application code that any enumerable properties have been added +for a reason and to omit hasOwnProperty, since it makes code more verbose and less +readable. In library code hasOwnProperty should still be used since +assumptions cannot be made about which enumerable properties might reside +on the prototype chain.

    + +

    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 with older versions of ECMAScript. +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.

    +

    In newer versions of ECMAScript, non-enumerable properties can be defined with +Object.defineProperty, reducing the risk of iterating over properties without +using hasOwnProperty. Nonetheless, care must be taken when using older +libraries like Prototype, which does not yet take advantage of new ECMAScript features. +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 in ECMAScript 3 or lower, as well as +in library code. Assumptions should never be made in these environments about whether +the native prototypes have been extended or not. Since ECMAScript 5, Object.defineProperty +makes it possible to define non-enumerable properties and to omit hasOwnProperty in +application code.

    +

    Functions

    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 an 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, 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 declared 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. 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, in 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 can be considered another +mis-design of the language because 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, you can create a +local variable inside of method that refers to Foo.

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

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

    +

    As of ECMAScript 5 you can use the bind method combined with an anonymous function to achieve the same result.

    +
    Foo.method = function() {
    +    var test = function() {
    +        // this now refers to Foo
    +    }.bind(this);
    +    test();
    +};
    +

    Assigning Methods

    +

    Another thing that does not work in JavaScript is function aliasing, which 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, in +fact, it is 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 an 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. +With closures, scopes always keep access to the outer scope, in which they +were defined. 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 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.hackFail = function() {
    +    count = 1337;
    +};
    +

    The above code will not change the variable count in the scope of Counter, +since foo.hackFail 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 loop's 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. At the time +console.log gets called, the for loop has already finished, and the value of +i has 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, which 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 other popular way to achieve this is to add an additional argument to +the setTimeout function, which passes these arguments to the callback.

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

    Some legacy JS environments (Internet Explorer 9 & below) do not support this.

    +

    There's yet another way to accomplish this by using .bind, which can bind +a this context and arguments to function. It behaves identically to the code +above

    +
    for(var i = 0; i < 10; i++) {
    +    setTimeout(console.log.bind(console, 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);
    +

    Because 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 turn methods - functions that use the +value of this as well as their arguments - into normal functions which only use their arguments.

    +
    function Person(first, last) {
    +  this.first = first;
    +  this.last = last;
    +}
    +
    +Person.prototype.fullname = function(joiner, options) {
    +  options = options || { order: "western" };
    +  var first = options.order === "western" ? this.first : this.last;
    +  var last =  options.order === "western" ? this.last  : this.first;
    +  return first + (joiner || " ") + last;
    +};
    +
    +// Create an unbound version of "fullname", usable on any object with 'first'
    +// and 'last' properties passed as the first argument. This wrapper will
    +// not need to change if fullname changes in number or order of arguments.
    +Person.fullname = function() {
    +  // Result: Person.prototype.fullname.call(this, joiner, ..., argN);
    +  return Function.call.apply(Person.prototype.fullname, arguments);
    +};
    +
    +var grace = new Person("Grace", "Hopper");
    +
    +// 'Grace Hopper'
    +grace.fullname();
    +
    +// 'Turing, Alan'
    +Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
    +

    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 only time the arguments object is not created is 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, but it also breaks encapsulation +because the function may now be dependent on a specific calling context.

    +

    Making use of arguments.callee or any of its properties is highly discouraged.

    + +

    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 Person(name) {
    +    this.name = name;
    +}
    +
    +Person.prototype.logName = function() {
    +    console.log(this.name);
    +};
    +
    +var sean = new Person();
    +

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

    +

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

    +
    function Car() {
    +    return 'ford';
    +}
    +new Car(); // a new object, not 'ford'
    +
    +function Person() {
    +    this.someValue = 2;
    +
    +    return {
    +        name: 'Charles'
    +    };
    +}
    +new Person(); // the returned object ({name:'Charles'}), not including someValue
    +

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

    +
    function Pirate() {
    +    this.hasEyePatch = true; // gets set on the global object!
    +}
    +var somePirate = Pirate(); // somePirate is 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 Robot() {
    +    var color = 'gray';
    +    return {
    +        getColor: function() {
    +            return color;
    +        }
    +    }
    +}
    +
    +new Robot();
    +Robot();
    +

    Both calls to Robot return the same thing, a newly created object that +has a property called getColor, which is a +Closure.

    +

    It should also be noted that the call new Robot() does not affect the +prototype of the returned object. While the prototype will be set on the newly +created object, Robot 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

    +

    It is often recommended to not use new because forgetting its use may +lead to bugs.

    +

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

    +
    function CarFactory() {
    +    var car = {};
    +    car.owner = 'nobody';
    +
    +    var milesPerGallon = 2;
    +
    +    car.setOwner = function(newOwner) {
    +        this.owner = newOwner;
    +    }
    +
    +    car.getMPG = function() {
    +        return milesPerGallon;
    +    }
    +
    +    return car;
    +}
    +

    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 +is contrary to 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 use it +consistently.

    +

    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 +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, which 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, +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 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 execution starts. JavaScript moves +the var statements, as well as function declarations, to the top of the +nearest surrounding scope.

    +
    // var statements got moved here
    +var bar, someValue; // default to 'undefined'
    +
    +// the function declaration 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, although the if statement seemed to modify the global +variable goo, it actually modifies the local variable - after hoisting +has been applied.

    +

    Without knowledge of hoisting, one might suspect the code below would raise a +ReferenceError.

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

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

    +
    var SomeImportantThing;
    +
    +// other code might initialize 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 the function.

    +

    For example, when trying to access a variable named foo inside the scope of a +function, JavaScript will look up 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 associated with having only one global namespace is the +likelihood 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 +be callable, they must first be evaluated.

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

    There are other ways to evaluate and directly call the function expression +which, while different in syntax, behave the same way.

    +
    // A few other styles for directly invoking the 
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +void function(){}();
    +// and so on...
    +

    In Conclusion

    +

    It is recommended to always use an anonymous wrapper to encapsulate code in +its own namespace. This does not only protect code against name clashes, but 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.

    +

    Arrays

    Array Iteration and Properties

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

    + +

    Because the for in loop enumerates all the properties that are on the prototype +chain and because 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, which 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 arr = [1, 2, 3, 4, 5, 6];
    +arr.length = 3;
    +arr; // [1, 2, 3]
    +
    +arr.length = 6;
    +arr.push(4);
    +arr; // [1, 2, 3, undefined, undefined, undefined, 4]
    +

    Assigning a smaller length truncates the array. Increasing it creates a sparse 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 use the array literal - [] 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 when 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
    +

    Being able to set the length of the array in advance is only useful in a few +cases, like repeating a string, in which it avoids the use of a loop.

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

    In Conclusion

    +

    Literals are preferred to the Array constructor. They are shorter, have a clearer syntax, and increase code +readability.

    +

    Types

    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 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 called equality operators, they behave +differently when at least one of their operands is 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 almost completely broken.

    +

    Although instanceof still has 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.

    + +

    The Class of an Object

    +

    The only way to determine an object's [[Class]] value is using Object.prototype.toString. It +returns a string in the following format: '[object ' + valueOfClass + ']', e.g [object String] or +[object Array]:

    +
    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 because 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 between implementations.

    +

    Unless checking whether a variable is defined, typeof should be avoided.

    +

    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 originate 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 originate 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 object is converted
    +                      // to a number primitive via implicit call of
    +                      // Number.prototype.valueOf method
    +
    +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';
    + +

    To avoid the issues 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, passing literals or non-object values 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 an empty string, a value can easily be cast 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 to a boolean.

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

    Core

    Why Not to Use eval

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

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

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

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

    The use of eval should be avoided. 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, because 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 should be questioned +in its workings, performance and security. If something requires eval in +order to work, it 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, null and undefined, with +the latter being more useful.

    +

    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. However, this variable is neither a constant +nor a keyword of the language. This means that its value can be easily +overwritten.

    + +

    Here are some examples of when the value undefined is returned:

    +
      +
    • Accessing the (unmodified) global variable undefined.
    • +
    • Accessing a declared but not yet initialized variable.
    • +
    • Implicit returns of functions due to missing return statements.
    • +
    • return statements that do not explicitly return anything.
    • +
    • Lookups of non-existent properties.
    • +
    • Function parameters that do not have any explicit value passed.
    • +
    • Anything that has been set to the value of undefined.
    • +
    • Any expression in the form of void(expression)
    • +
    +

    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.

    +

    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 `undefined`
    +
    +})('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 here is 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, so it is possible to omit them.

    +

    JavaScript is not a semicolon-less language. In fact, it 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 because 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 recommended +that braces be kept on the same line as their corresponding statements and to +never omit them for single-line if / else statements. These measures will +not only improve the consistency of the code, but they will also prevent the +JavaScript parser from changing code behavior.

    +

    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 it is a property of either the Activation object or +the Global object. Such properties have a set of attributes, one of which is +DontDelete. Variable and function declarations in global and function code +always create properties with DontDelete, and 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

    +

    Explicitly set properties can be deleted normally.

    +
    // 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 attribute. That's why the 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 here refers +to the Global object and we explicitly declare variable a as its property +which allows us to delete it.

    +

    IE (at least 6-8) has some bugs, so the code above doesn't work.

    +

    Function arguments and built-ins

    +

    Functions' normal arguments, arguments objects +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

    +

    The behaviour of delete operator can be unpredictable for hosted objects. Due +to the specification, host objects are allowed to implement any kind of behavior.

    +

    In conclusion

    +

    The delete operator often has unexpected behaviour and can only be safely +used to delete explicitly set properties on normal objects.

    +

    Other

    setTimeout and setInterval

    Since JavaScript is asynchronous, it is possible to schedule the execution of a +function using the setTimeout and setInterval functions.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // returns a Number > 0
    +

    When setTimeout is called, it returns the ID of the timeout and schedule +foo to run approximately one thousand milliseconds in the future. +foo will then be executed once.

    +

    Depending on the timer resolution of the JavaScript engine 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 specified in the setTimeout call.

    +

    The function that was passed as the first parameter will get called by the +global object, which means that this inside the called function +refers to the global 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, 1000);
    +

    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 solution, as well as most controllable solution, is to use setTimeout within +the function itself.

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

    Not only does this encapsulate the setTimeout call, but it also prevents the +stacking of calls and 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 on 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);
    +}
    +

    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 every time you call setTimeout.

    +
    // clear "all" timeouts
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    Even though this works on all major browsers today, 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

    +

    setTimeout and setInterval can also take a string as their first parameter. +This feature should never be used because 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 be 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 to pass 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(1, 2, 3);
    +}, 1000)
    + +

    In Conclusion

    +

    A string should never 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.

    +

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

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/it/index.html b/external/JavaScript-Garden/it/index.html new file mode 100644 index 0000000..2de414c --- /dev/null +++ b/external/JavaScript-Garden/it/index.html @@ -0,0 +1,1616 @@ +JavaScript Garden

    Introduzione

    Introduzione

    JavaScript Garden è una collezione in continua crescita di documentazione +relativa alle parti più peculiari del linguaggio di programmazione JavaScript. +Il suo intento è quello di mostrare come evitare i più comuni errori, i +problemi legati alla performance e le cattive abitudini che i programmatori +JavaScript non esperti possono incontrare lungo il loro cammino di +approfondimento del linguaggio.

    +

    L'obiettivo di JavaScript Garden non è quello di insegnarti JavaScript. +Una conoscenza pregressa del linguaggio è fortemenete consigliata, in modo da +capire gli argomenti trattati da questa guida. Per poter imparare le basi del +linguaggio, ti suggeriamo di leggere l'eccellente guida su Mozilla +Developer Network.

    +

    Gli autori

    +

    Questa guida è il risultato del lavoro di due utenti di Stack Overflow, +Ivo Wetzel (stesura) e Zhang Yi Jiang (progettazione).

    +

    È attualmente mantenuto da Tim Ruffles.

    +

    Collaboratori

    + +

    Hosting

    +

    JavaScript Garden è ospitato su GitHub, ma Cramer Development ci supporta +con un mirror su JavaScriptGarden.info.

    +

    Licenza

    +

    JavaScript Garden è pubblicato sotto la licenza MIT ed ospitato su +GitHub. Se trovi inesattezze o errori di battitura, ti prego di +segnalare il problema o fare un pull request sul nostro repository. +Puoi anche trovarci nella stanza JavaScript della chat di Stack +Overflow.

    +

    Oggetti

    Utilizzo di oggetti e proprietà

    Tutto in JavaScript funziona come un oggetto, con la sola eccezione di +null e undefined.

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

    Un'idea comunemente errata è che i numeri letterali non possano essere +usati come oggetti. Questo a causa di una scorretta gestione da parte del +parser di JavaScript, che tenta di analizzare la dot notation di un +numero come se fosse un letterale in virgola mobile.

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

    Esistono un paio di soluzioni che possono essere usate per far sì che i +numeri letterali vengano considerati come oggetti.

    +
    2..toString(); // il secondo punto viene correttamente riconosciuto
    +2 .toString(); // notate lo spazio tra il numero e il punto
    +(2).toString(); // viene prima valutato 2
    +

    Oggetti come un tipo di dato

    +

    Gli oggetti in JavaScript possono anche essere usati come tabelle hash e +consistono principalmente di proprietà con un nome che mappano dei valori.

    +

    Usando un oggetto letterale (notazione {}) è possibile creare un +semplice oggetto. Questo nuovo oggetto eredita da +Object.prototype e non ha proprietà definite.

    +
    var foo = {}; // un nuovo oggetto vuoto
    +
    +// un nuovo oggetto con una proprietà `test` con valore 12
    +var bar = {test: 12};
    +

    Accedere alle proprietà

    +

    È possibile accedere alle proprietà di un oggetto in due modi. +Usando il punto oppure attraverso l'uso delle parentesi quadre.

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

    Le due notazioni funzionano quasi in modo identico, con la sola differenza +che usando le parentesi quadre è possibile impostare dinamicamente le +proprietà ed il loro nome identificatore, cosa che altrimenti genererebbe +un errore di sintassi.

    +

    Cancellazione delle proprietà

    +

    Il solo modo per rimuovere una proprietà da un oggetto è quello di usare +l'operatore delete. Impostando la proprietà a undefined o null, infatti, +si rimuove solo il valore associato alla proprietà, ma non la chiave.

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

    Il codice qui sopra, stamperà sia bar undefined che foo null. Soltanto +baz è stato rimosso, e quindi non compare nell'output.

    +

    Notazione delle chiavi

    +
    var test = {
    +    'case': 'Parola chiave, scrivimi come stringa',
    +    // solleva SyntaxError
    +    delete: 'Parola chiave, anche io devo essere una stringa'
    +};
    +

    Le proprietà di un oggetto possono essere scritte sia come normali caratteri +che come stringhe. A causa di un altro errore di progettazione del parser di +JavaScript, il codice appena visto genererà un SyntaxError in ECMAScript +precedente alla versione 5.

    +

    Questo errore nasce dal fatto che delete è una parola chiave, quindi, +deve essere scritta come una stringa letterale per assicurarsi che venga +correttamente interpretata dai vecchi motori JavaScript.

    +

    Il prototipo

    JavaScript non segue il classico modello di ereditarietà ma, piuttosto, +utilizza quello prototipale.

    +

    Anche se ciò viene considerato come uno dei punti più deboli del JavaScript, +il modello ad ereditarietà prototipale è difatto più potente di quello +classico. Ad esempio, è piuttosto semplice creare un modello classico +sulle basi di quello prototipale, mentre l'operazione inversa è piuttosto +complessa.

    +

    JavaScript è il solo linguaggio ampiamente utilizzato che sfrutta l'ereditarietà +prototipale, quindi è possibile prendersi il proprio tempo per adeguarsi alle +differenze esistenti tra i due modelli.

    +

    La prima grande differenza è che l'ereditarietà in JavaScript utilizza le +catene di prototipi.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Imposta il prototipo di Bar ad una nuova istanza di Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Si assicura di elencare Bar come l'attuale costruttore
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // crea una nuova istanza di bar
    +
    +// La catena di prototipi finale
    +test [istanza di Bar]
    +    Bar.prototype [istanza di Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* ecc. */ }
    +

    Nel codice qui sopra, l'oggetto test erediterà sia da Bar.prototype che da +Foo.prototype, e quindi avrà accesso alla funzione method che era stata +definita in Foo. Avrà anche accesso alla proprietà value dell'unica +istanza di Foo, cioè il suo prototipo. È importante notare come +new Bar() non crei una nuova istanza di Foo, ma piuttosto riutilizzi +quella assegnata al suo prototipo. Perciò, tutte le istanze di Bar +condivideranno la stessa proprietà value.

    + +

    Tabella delle proprietà

    +

    Quando si accede alle proprietà di un oggetto, JavaScript risale la +catena di prototipi fino a che non incontra una proprietà con il nome +richiesto.

    +

    Se raggiunge la cima della catena (cioè Object.prototype) senza aver +trovato le specifica proprietà, ritorna il valore undefined.

    +

    La proprietà Prototype

    +

    Anche se la proprietà prototype viene usata dal linguaggio per creare la +catena di prototipi, è comunque sempre possibile assegnarvi un qualsiasi +dato valore. Nonostante cio, i dati primitivi verranno semplicemente ignorati +quando assegnati ad un prototipo.

    +
    function Foo() {}
    +Foo.prototype = 1; // nessun effetto
    +

    L'assegnazione di oggetti, come mostrato nell'esempio precedente, funzionerà, +e permette la creazione dinamica di catene di prototipi.

    +

    Performance

    +

    Il tempo di ricerca per proprietà presenti in alto (all'inizio) della catena +di prototipi, può avere un impatto negativo sulla performance, e questo deve +essere tenuto bene in considerazione in codice dove la performance è un fattore +critico. Inoltre, il tentativo di accedere a proprietà inesistenti obbligherà +comunque ad attraversare tutta la catena di prototipi.

    +

    Oltre a ciò, iterando tra le proprietà di un oggetto, +ogni proprietà presente nella catena di prototipi verrà enumerata.

    +

    Estensione di prototipi nativi

    +

    Una caratteristica che viene spesso abusata, è quella di estendere +Object.prototype o uno degli altri prototipi interni al linguaggio.

    +

    Questa tecnica viene detta monkey patching e vìola il principio di +incapsulamento. Anche se usata da popolari framework come Prototype, +non c'è una valida ragione per pasticciare, aggiungendo ai tipi interni del +linguaggio funzionalità non standard.

    +

    La sola buona ragione per estendere un prototipo interno è quella di +effettuare il backport di funzionalità presenti nei motori JavaScript +più recenti, come ad esempio Array.forEach.

    +

    In conclusione

    +

    È essenziale capire il modello di ereditarietà prototipale prima +di scrivere codice complesso che ne faccia uso. Bisogna, inoltre, tenere +sotto controllo la lunghezza della catena di prototipi nel proprio codice, +e suddividerla in più catene se necessario, per evitare possibili problemi di +performance. Inoltre, i prototipi nativi non dovrebbero mai essere +estesi a meno che non sia per garantire compatibilità con le funzionalità +più recenti di JavaScript.

    +

    hasOwnProperty

    Per verificare se un oggetto ha (possiede) una proprietà definita dentro +se stesso piuttosto che in qualche parte della sua +catena di prototipi, è necessario usare il metodo +hasOwnProperty che tutti gli oggetti ereditano da Object.prototype.

    + +

    hasOwnProperty è la sola cosa in JavaScript che si occupa delle proprietà +senza attraversare la catena di prototipi.

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

    Solo hasOwnProperty darà il risultato atteso e corretto. Guarda la sezione +[cicli for in][#object.forinloop] per maggiori dettagli riguardo a quando +usare hasOwnProperty per iterare le proprietà di un oggetto.

    +

    hasOwnProperty come proprietà

    +

    JavaScript non protegge il nome di proprietà hasOwnProperty. Quindi, se +esiste la possibilità che un oggetto possa avere una proprietà con questo +nome, è necessario usare un hasOwnProperty esterno per ottenere il +risultato corretto.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // ritorna sempre false
    +
    +// Usa un altro hasOwnProperty di Object e lo richiama con 'this' impostato a foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    +// E' anche possibile usare hasOwnProperty dal prototipo di
    +// Object per questo scopo
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
    +

    In conclusione

    +

    Usare hasOwnProperty è l'unico metodo affidabile per verificare +l'esistenza di una proprietà in un oggetto. È raccomandabile che +hasOwnProperty venga usata in molti casi in cui è necessario iterare le +proprietà di un oggetto, come descritto nella sezione cicli for in.

    +

    Il ciclo for in

    Come per l'operatore in, il ciclo for in attraversa la catena di +prototipi quando itera tra le proprietà di un oggetto.

    + +
    // Modifichiamo Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // stampa sia bar che moo
    +}
    +

    Dato che non è possibile modificare il comportamento del ciclo for in, +è necessario filtrare le proprietà indesiderate all'interno del ciclo stesso. +In ECMAScript 3 o precedente, questo può essere fatto usando il metodo +hasOwnProperty di Object.prototype.

    +

    A partire da ECMAScript 5, Object.defineProperty può essere utilizzato con +enumerbale impostato a false per aggiungere proprietà agli oggetti (incluso +Object) senza che queste proprietà vengano enumerate. In questo caso è +ragionevole assumere che, nel codice di un'applicazione, ogni proprietà +enumerabile sia stata aggiunta per un motivo, ed quindi omettere hasOwnProperty +in quanto rende il codice più prolisso e meno leggibile. Nel codice delle +librerie hasOwnProperty dovrebbe essere ancora utilizzato, dato che non è +possibile presumere quali proprietà enumerabili siano presenti nella catena dei +prototipi.

    + +

    Usare hasOwnProperty per il filtraggio

    +
    // questo è il foo dell'esempio precedente
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Questa è la sola versione corretta da usare con le vecchie versioni di ECMAScript. +Proprio a causa dell'utilizzo di hasOwnProperty, soltanto moo verrà +stampato; mentre omettendone l'uso, il codice sarà soggetto ad errori nei casi +dove i prototipi nativi (ad esempio Object.prototype) sono stati estesi.

    +

    Nelle nuove versioni di ECMAScript, le proprietà non enumerabili possono essere +definite con Object.defineProperty, riducendo il rischio di iterare sulle +proprietà non usando hasOwnProperty. È altresì importante stare attenti +quando si usano librerie come Prototype, che ancora non sfruttano le nuove +funzionalità di ECMAScript. +Quando questo framework viene incluso, è sicuro che i cicli for in che non +utilizzano hasOwnProperty non funzioneranno.

    +

    In conclusione

    +

    Si raccomanda di usare sempre hasOwnProperty in ECMAScript 3 o precedenti, +e nel codice delle librerie. Non si dovrebbe mai dare per scontato nell'ambiente +in cui il codice sta girando, se i prototipi nativi sono stati estesi o meno. A +partire da ECMAScript 5 Object.defineProperty rende possibile definire proprietà +non enumerabili ed omettere hasOwnProperty nel codice dell'applicazione.

    +

    Funzioni

    Dichiarazioni ed espressioni di funzione

    Le funzioni in JavaScript sono oggetti di prima classe. Ciò significa che +possono essere usate come ogni altro valore. Un uso comune di questa +caratteristica è quello di passare una funzione anonima come funzione di +callback ad un'altra funzione, possibilmente asincrona.

    +

    La dichiarazione di function

    +
    function foo() {}
    +

    La funzione qui sopra viene elevata (hoisted) prima +che inizi l'esecuzione del programma. Questo vuol dire che essa è disponibile +da un qualsasi punto dello scope in cui è stata definita, anche se +richiamata prima dell'effettiva definizione nel sorgente.

    +
    foo(); // funziona perché foo è stata creata prima di eseguire il codice
    +function foo() {}
    +

    L'espressione function

    +
    var foo = function() {};
    +

    Questo esempio assegna la funzione anonima alla variabile foo.

    +
    foo; // 'undefined'
    +foo(); // questo solleva un TypeError
    +var foo = function() {};
    +

    Dato che var è una dichiarazione che eleva il nome di variabile foo +prima che l'esecuzione del codice inizi, foo è già dichiarata quando lo +script viene eseguito.

    +

    Ma, dal momento che le assegnazioni avvengono solo a runtime, il valore di +foo sarà undefined per default, prima che il relativo +codice sia eseguito.

    +

    Espressione di funzione con nome

    +

    Un altro caso speciale è l'assegnazione di funzioni con nome.

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

    Qui, bar non è disponibile nello scope più esterno, dal momento che la +funzione viene assegnata solo a foo, mentre è disponibile all'interno di +bar. Ciò è dato dal modo in cui funziona la risoluzione dei nomi +in JavaScript: il nome della funzione è sempre reso disponibile nello scope +locale della funzione stessa.

    +

    Come funziona this

    JavaScript ha una concezione differente di ciò a cui il nome speciale this +fa normalmente riferimento nella maggior parte degli altri linguaggi di +programmazione. Ci sono esattamente cinque differenti modi nei quali +il valore di this può essere associato nel linguaggio.

    +

    Lo scope globale

    +
    this;
    +

    Usando this nello scope globale, esso farà semplicemente riferimento +all'oggetto globale.

    +

    Richiamando una funzione

    +
    foo();
    +

    Qui, this farà ancora riferimento all'oggetto globale.

    + +

    Richiamando un metodo

    +
    test.foo();
    +

    In questo esempio, this farà riferimento a test.

    +

    Richiamando un costruttore

    +
    new foo();
    +

    Una chiamata di funzione che viene preceduta dalla parola chiave new +agisce come un costruttore. Dentro la funzione, +this farà riferimento all'Object appena creato.

    +

    Impostazione esplicita di this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // l'array verrà espanso come mostrato sotto
    +foo.call(bar, 1, 2, 3); // risulterà in a = 1, b = 2, c = 3
    +

    Quando si usano i metodi call o apply di Function.prototype, il valore di +this all'interno della funzione chiamata viene esplicitamente impostato +al primo argomento della corrispondente chiamata di funzione.

    +

    Come risultato, nell'esempio sopra, il caso del metodo non viene applicato, +e this all'interno di foo sarà impostato a bar.

    + +

    Insidie comuni

    +

    Mentre molti di questi casi hanno senso, il primo può essere considerato +un altro errore di progettazione del linguaggio perché non ha mai un +uso pratico.

    +
    Foo.method = function() {
    +    function test() {
    +        // this viene impostato all'oggetto globale
    +    }
    +    test();
    +};
    +

    Una comune credenza è che this all'interno di test faccia riferimento a +Foo mentre, invece, non è così.

    +

    Per poter ottenere l'accesso a Foo dall'interno di test, si può creare +una variabile locale all'interno di method che faccia riferimento a Foo.

    +
    Foo.method = function() {
    +    var self = this;
    +    function test() {
    +        // Qui viene usato self invece di this
    +    }
    +    test();
    +};
    +

    self è solo un normale nome di variabile, ma viene comunemente usato come +riferimento ad un this più esterno. Abbinato alle closures +può anche essere usato per passare il valore di this.

    +

    Con l'introduzione di ECMAScript 5 è possibile usare il metodo bind combinato +con una funziona anonima

    +
    Foo.method = function() {
    +    var test = function() {
    +        // this ora fa riferimento a Foo
    +    }.bind(this);
    +    test();
    +};
    +

    Metodi di asseganzione

    +

    Un'altra cosa che non funziona in JavaScript è la creazione di un alias ad +una funzione, cioè l'assegnazione di un metodo ad una variabile.

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

    A causa della prima dichiarazione, test ora agisce da semplice chiamata a +funzione e quindi, this all'interno di essa non farà più riferimento a +someObject.

    +

    Mentre l'assegnazione tardiva di this potrebbe sembrare una cattiva idea +in un primo momento, alla prova dei fatti è ciò che fa funzionare +l'ereditarietà prototipale.

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

    Quando method viene chiamato da un'istanza di Bar, this farà riferimento +a quell'istanza.

    +

    Closures e riferimenti

    Una delle caratteristiche più potenti di JavaScript è la disponibilità delle +closure. Con le closure, gli scope hanno sempre accesso allo scope +più esterno nel quale sono state definite. Dal momento che il solo scope che +JavaScript ha è lo scope di funzione, tutte le funzioni, +per default, agiscono da closure.

    +

    Emulare variabili private

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

    Qui, Counter ritorna due closure: la funzione increment e get. +Entrambe mantengono un riferimento allo scope di Counter e, quindi, +hanno sempre accesso alla variabile count definita in quello scope.

    +

    Perché le variabili private funzionano

    +

    Dato che non è possibile fare riferimento o assegnare scope in JavaScript, +non c'è modo per accedere alla variabile count dall'esterno. Il solo +modo per interagire con essa è tramite le due closure.

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

    Il codice sopra non modificherà la variabile count nello scope di Counter, +dato che foo.hack non è stato definito in quello scope. Invece, creerà +(o meglio, sostituirà) la variabile globale count.

    +

    Closure nei cicli

    +

    Un errore che spesso viene fatto è quello di usare le closure all'interno dei +cicli, come se stessero copiando il valore della variabile dell'indice del ciclo.

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

    Questo esempio non stamperà i numeri da 0 a 9, ma semplicemente il +numero 10 dieci volte.

    +

    La funzione anonima mantiene un riferimento ad i, ma al momento in cui +console.log viene richiamata, il ciclo for è già terminato, ed il valore +di i è stato impostato a 10.

    +

    Per ottenere l'effetto desiderato, è necessario creare una copia del valore +di i.

    +

    Evitare il problema del riferimento

    +

    Per copiare il valore della variabile indice del ciclo, è meglio usare un +contenitore anonimo.

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

    La funzione anonima più esterna viene chiamata immediatamente con i come +suo primo argomento e riceverà una copia del valore di i come suo +parametro e.

    +

    La funzione anonima che viene passata a setTimeout ora ha un riferimento a +e, il cui valore non viene modificato dal ciclo.

    +

    C'è anche un altro possibile modo per ottenere il medesimo risultato, e cioè +ritornare una funzione dal contenitore anonimo che avrà quindi lo stesso +comportamento del codice visto precedentemente.

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

    C'è un ulteriore modo per ottenere ciò, usando .bind, che può assegnare un +contesto this e degli argomenti ad una funzione. Esso funziona allo stesso +modo degli esempi precedenti

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

    L'oggetto arguments

    Ogni scope di funzione in JavaScript può accedere alla speciale variabile +arguments. Questa variabile mantiene un elenco di tutti gli argomenti +che sono stati passati alla funzione.

    + +

    L'oggetto arguments non è un Array. Sebbene abbia in parte la +semantica di un array (nello specifico la proprietà length), esso non +eredita da Array.prototype ed è a tutti gli effetti un Object.

    +

    Proprio per questo motivo, non è possibile usare su arguments i metodi +standard degli array come push, pop, slice. E mentre l'iterazione con +un semplice ciclo for funzionerà senza problemi, sarà necessario convertire +l'oggetto in un vero Array per poter usare i metodi standard di Array con +esso.

    +

    Conversione ad array

    +

    Il codice seguente ritornerà un nuovo Array contenenente tutti gli elementi +dell'oggetto arguments.

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

    Dato che questa conversione è lenta, non è raccomandato usarla in sezioni +di codice in cui la performance è un fattore critico.

    +

    Passaggio di argomenti

    +

    Quello che segue è il metodo raccomandato per passare argomenti da una funzione +ad un'altra.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // codice da eseguire
    +}
    +

    Un altro trucco è quello di usare call e apply insieme per creare veloci +contenitori senza vincoli.

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Crea una versione senza vincoli di "method"
    +// Richiede i parametri: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Risultato: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    Parametri formali e indici degli argomenti

    +

    L'oggetto arguments crea funzioni getter e setter sia per le sue +proprietà che per i parametri formali della funzione.

    +

    Come risultato, la modifica del valore di un parametro formale modificherà +anche il valore della corrispondente proprietà nell'oggetto arguments, e +vice versa.

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

    Miti e verità sulla performance

    +

    Il solo caso in cui l'oggetto arguments non viene creato, è quando esso +viene dichiarato come un nome all'interno di una funzione o uno dei suoi +parametri formali. Non importa che venga usato o meno.

    +

    Sia i getter che i setter vengono sempre creati. Perciò, il loro +utilizzo non ha praticamente alcun impatto sulle prestazioni, specialmente +nel mondo reale dove nel codice c'è più di un semplice accesso alle proprietà +dell'oggetto arguments.

    + +

    Ad ogni modo, c'è un caso che ridurrà drasticamente la performance nei motori +JavaScript moderni. È il caso dell'utilizzo di arguments.callee.

    +
    function foo() {
    +    arguments.callee; // fa qualcosa con questo oggetto funzione
    +    arguments.callee.caller; // e l'oggetto funzione chiamante
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // normalmente sarebbe sostituito con il suo codice...
    +    }
    +}
    +

    Nel codice qui sopra, foo non può più essere soggetto ad inlining +dal momento che necessita di conoscere sia se stesso che il suo chiamante. +Questo non solo annulla possibili guadagni prestazionali ottenibili con +l'inlining, ma spezza anche il principio di incapsulazione perché la funzione +ora potrebbe essere dipendente da uno specifico contesto di esecuzione.

    +

    L'utilizzo di arguments.callee o di qualsiasi altra delle sue proprietà +è altamente sconsigliato.

    + +

    Costruttori

    I costruttori in JavaScript sono differenti da quelli di molti altri linguaggi. +Qualsiasi chiamata a funzione preceduta dalla parola chiave new agisce come +un costruttore.

    +

    Dentro al costruttore (la funzione chiamata) il valore di this fa riferimento +al nuovo oggetto creato. Il prototype di questo nuovo +oggetto viene impostato al prototype dell'oggetto funzione che è stato invocato +come costruttore.

    +

    Se la funzione che è stata chiamata non ha un'istruzione return esplicita, +allora essa ritorna implicitamente il valore di this (il nuovo oggetto).

    +
    function Person(name) {
    +    this.name = name;
    +}
    +
    +Person.prototype.logName = function() {
    +    console.log(this.name);
    +};
    +
    +var sean = new Person();
    +

    Questo esempio chiama Person come costruttore ed imposta il prototype del +nuovo oggetto creato a Person.prototype.

    +

    In caso di istruzione return esplicita, la funzione ritorna il valore +specificato da quell'istruzione, ma solo se il valore di ritorno è un +Object.

    +
    function Car() {
    +    return 'ford';
    +}
    +new Car(); // un nuovo oggetto, non 'ford'
    +
    +function Person() {
    +    this.someValue = 2;
    +
    +    return {
    +        name: 'Charles'
    +    };
    +}
    +new Person(); // l'oggetto ritornato ({name: 'Charles'}), escluso someValue
    +

    Quando la parola chiave new viene omessa, la funzione non ritornerà un +nuovo oggetto.

    +
    function Pirate() {
    +    this.hasEyePatch = true; // imposta la proprietà nell'oggetto globale!
    +}
    +var somePirate = Pirate(); // somePirate è undefined
    +

    Mentre l'esempio precedente potrebbe sembrare essere funzionante in alcuni +casi, a causa del modo in cui lavora this in JavaScript, +esso userà l'oggetto globale come valore di this.

    +

    Factory (Fabbriche di oggetti)

    +

    Per poter omettere la parola chiave new, la funzione costruttore deve +esplicitamente ritornare un valore.

    +
    function Robot() {
    +    var color = 'gray';
    +    return {
    +        getColor: function() {
    +            return color;
    +        }
    +    }
    +}
    +Robot.prototype = {
    +    someFunction: function() {}
    +};
    +
    +new Robot();
    +Robot();
    +

    Entrambe le chiamate a Robot ritornano lo stesso risultato, un nuovo oggetto +creato con una proprietà chiamata method, che è una Closure.

    +

    Bisogna anche notare che la chiamata new Robot() non influisce sul prototipo +dell'oggetto ritornato. Mentre il prototipo sarà impostato con il nuovo oggetto +creato, Robot non ritornerà mai quel nuovo oggetto.

    +

    Nell'esempio sopra, non c'è differenza funzionale nell'usare o meno la parola +chiave new.

    +

    Creare nuovi oggetti tramite factory

    +

    Viene spesso raccomandato di non usare new perché una sua dimenticanza +può portare a bug potenzialmente insidiosi da risolvere.

    +

    Per poter creare un nuovo oggetto, si dovrebbe invece usare una factory e +costruire un nuovo oggetto all'interno di quella factory.

    +
    function CarFactory() {
    +    var car = {};
    +    car.owner = 'nobody';
    +
    +    var milesPerGallon = 2;
    +
    +    car.setOwner = function(newOwner) {
    +        this.owner = newOwner;
    +    }
    +
    +    car.getMPG = function() {
    +        return milesPerGallon;
    +    }
    +    return car;
    +}
    +

    Sebbene questo esempio sia a prova di omissione della parola chiave new e +renda sicuramente più semplice l'utilizzo delle variabili private, +esso ha alcuni aspetti negativi.

    +
      +
    1. Usa più memoria dal momento che gli oggetti creati non condividono +i metodi di un prototipo.
    2. +
    3. Per poter ereditare, la factory deve copiare tutti i metodi da un altro +oggetto oppure mettere quell'oggetto nel prototipo del nuovo oggetto.
    4. +
    5. Perdere la catena di prototipi solo perché si vuole tralasciare la +parola chiave new è contrario allo spirito del linguaggio.
    6. +
    +

    In conclusione

    +

    Sebbene l'omissione della parola chiave new possa portare all'introduzione di +bug, non è certo un motivo per privarsi completamente dell'uso dei prototipi. +Alla fine si tratta di decidere quale sia la soluzione più adatta per +l'applicazione. È specialmente importante scegliere uno specifico stile +di creazione degli oggetti ed usarlo in maniera consistente.

    +

    Scope e spazi di nome (namespace)

    Sebbene JavaScript non abbia problemi con la sintassi delle parentesi +graffe per la definizione di blocchi, esso non supporta lo scope +per blocco, quindi, tutto ciò che il linguaggio ci mette a disposizione +è lo scope di funzione.

    +
    function test() { // questo è uno scope
    +    for(var i = 0; i < 10; i++) { // questo non è uno scope
    +        // conta
    +    }
    +    console.log(i); // 10
    +}
    + +

    Anche gli spazi di nome (namespace) non sono gestiti in JavaScript, e ciò +significa che ogni cosa viene definita in un namespace globalmente condiviso.

    +

    Ogni volta che ci si riferisce ad una variabile, JavaScript risale attraverso +tutti gli scope fino a che non la trova e, nel caso esso raggiunga lo scope +globale senza aver trovato il nome richiesto, solleva un ReferenceError.

    +

    Il problema delle variabili globali

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

    Questi due script non hanno lo stesso effetto. Lo script A definisce una +variabile chiamata foo nello scope globale, mentre lo script B definisce +una foo nello scope attuale.

    +

    Ancora una volta. Questo esempio non sortisce lo stesso effetto: il +non utilizzo di var può avere importanti conseguenze.

    +
    // scope globale
    +var foo = 42;
    +function test() {
    +    // scope locale
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    L'omissione dell'istruzione var all'interno della funzione test sostituirà +il valore di foo. Sebbene questo possa non sembrare un grosso problema in +un primo momento, ritrovarsi con migliaia di linee di JavaScript senza +utilizzare var introdurrà orribili bug molto difficili da individuare.

    +
    // scope globale
    +var items = [/* un elenco */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // scope di subLoop
    +    for(i = 0; i < 10; i++) { // istruzione var omessa
    +        // fai qualcosa di eccezionale!
    +    }
    +}
    +

    Il ciclo esterno terminerà dopo la prima chiamata a subLoop, dato che subLoop +sovrascriverà il valore globale di i. L'utilizzo di una var per il secondo ciclo +for avrebbe facilmente evitato questo errore. L'istruzione var non dovrebbe +mai essere omessa a meno che l'effetto desiderato non sia proprio quello +di influenzare lo scope esterno.

    +

    Variabili locali

    +

    In JavaScript le sole sorgenti per le variabili locali sono i parametri +funzione e le variabili dichiarate tramite l'istruzione +var.

    +
    // scope globale
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // scope locale della funzione test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    Mentre foo e i sono variabili locali all'interno dello scope della funzione +test, l'assegnazione di bar sostituirà la variabile globale con lo stesso +nome.

    +

    Elevamento (hoisting)

    +

    JavaScript eleva le dichiarazioni. Questo significa che le istruzioni var +e le dichiarazioni function verranno spostate in cima agli scope che le +racchiudono.

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

    Il codice qui sopra, viene trasformato prima che inizi l'esecuzione. JavaScript +sposta sia le istruzioni var che le dichiarazioni function in cima al più +vicino scope che le racchiude.

    +
    // le istruzioni var vengono spostate qui
    +var bar, someValue; // di default a 'undefined'
    +
    +// la dichiarazione function viene spostata qui
    +function test(data) {
    +    var goo, i, e; // il blocco scope mancante sposta qui queste istruzioni
    +    if (false) {
    +        goo = 1;
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // fallisce con un TypeError dato che bar è ancora 'undefined'
    +someValue = 42; // le assegnazioni non vengono influenzate dall'elevazione
    +bar = function() {};
    +
    +test();
    +

    L'omissione del blocco di scope non solo muoverà le istruzioni var fuori dal +corpo dei cicli, ma renderà anche i risultati di certi costrutti if poco +intuitivi.

    +

    Nel codice originale, sebbene l'istruzione if sembrasse modificare la +variabile globale goo, effettivamente essa va a modificare la variabile locale +(dopo che l'elevazione è stata eseguita).

    +

    Senza la conoscenza dell'elevazione, uno potrebbe pensare che il codice +qui sotto sollevi un ReferenceError.

    +
    // verifica se SomeImportantThing è stato inizializzato
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Ma ovviamente tutto funziona grazie al fatto che l'istruzione var è stata +spostata all'inzio dello scope globale.

    +
    var SomeImportantThing;
    +
    +// qui altro codice potrebbe o meno inizializzare SomeImportantThing
    +
    +// ci assicuriamo che ci sia
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Ordine di risoluzione dei nomi

    +

    Tutti gli scope in JavaScript, scope globale incluso, hanno lo speciale +nome this definito in essi, che fa riferimento +all'oggetto attuale.

    +

    Gli scope di funzione hanno anche il nome arguments +definito in essi, che contiene gli argomenti passati alla funzione.

    +

    Per esempio, cercando di accedere ad una variabile di nome foo all'interno +dello scope di una funzione, JavaScript effettuerà una ricerca del nome nel +seguente ordine:

    +
      +
    1. Nel caso ci sia un'istruzione var foo nello scope attuale, usa quella.
    2. +
    3. Se uno dei parametri funzione si chiama foo, usa quello.
    4. +
    5. Se la funzione stessa si chiama foo, usa quella.
    6. +
    7. Vai al successivo scope esterno e ricomincia dal numero 1.
    8. +
    + +

    Spazi di nome (Namespace)

    +

    Un comune problema associato al fatto di avere un solo spazio nomi globale, +è che facilmente si incappa in problemi dove i nomi di variabile si +sovrappongono. In JavaScript queso problema può essere facilmente evitato +con l'aiuto dei contenitori anonimi.

    +
    (function() {
    +    // "namespace" auto contenuto
    +
    +    window.foo = function() {
    +        // una closure esposta
    +    };
    +
    +})(); // esecue immediatamente la funzione
    +

    Le funzioni anonime sono considerate espressioni, quindi +per poter essere richiamabili, esse devono prima essere valutate.

    +
    ( // valuta la funzione dentro le parentesi
    +function() {}
    +) // e ritorna l'oggetto funzione
    +() // richiama il risultato della valutazione
    +

    Ci sono altri modi per valutare e chiamare direttamente l'espressione funzione +i quali, sebbene differenti nella sintassi, hanno tutti il medesimo effetto.

    +
    // Alcuni modi per invocare direttamente la
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +// e così via...
    +

    In conclusione

    +

    Si raccomanda sempre di usare un contenitore anonimo per incapsulare il +codice nel suo proprio namespace. Questo non solo protegge il codice da +eventuali conflitti con i nomi, ma permette anche una migliore modularizzazione +dei programmi.

    +

    Inoltre, l'uso delle variabili globali è considerato una cattiva pratica. +Qualsiasi loro uso indica codice scritto male che è suscettibile ad errori +e difficile da mantenere.

    +

    Array

    Iterazione e proprietà degli Array

    Sebbene gli array in JavaScript siano oggetti, non ci sono valide ragioni +per usare il ciclo for in. Infatti, ci sono varie +buone ragioni per evitare l'utilizzo di for in con gli array.

    + +

    Dato che il ciclo for in enumera tutte le proprietà che sono presenti nella +catena di prototipi, e dal momento che il solo modo per escludere queste +proprietà è quello di usare hasOwnProperty, +esso è già venti volte più lento di un normale ciclo for.

    +

    Iterazione

    +

    Per poter ottenere la miglior performance durante l'iterazione degli array, +è meglio usare il classico ciclo for.

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

    In questo esempio c'è un ulteriore particolare da notare, che è il caching +della lunghezza dell'array tramite l = list.length.

    +

    Sebbene la proprietà length sia definita nell'array stesso, c'è ancora un +sovraccarico di lavoro dato dal fatto che deve essere ricercata ad ogni +iterazione del ciclo. E mentre i motori JavaScript recenti potrebbero +applicare delle ottimizzazioni in questo caso, non c'è modo di dire se il +codice verrà eseguito su uno di questi nuovi motori oppure no.

    +

    Infatti, l'omissione della parte di caching può risultare in un ciclo eseguito +soltanto alla metà della velocità con cui potrebbe essere eseguito facendo +il caching della lunghezza.

    +

    La proprietà length

    +

    Mentre il getter della proprietà length ritorna semplicemente il numero di +elementi che sono contenuti nell'array, il setter può essere usato per +troncare l'array.

    +
    var arr = [1, 2, 3, 4, 5, 6];
    +arr.length = 3;
    +arr; // [1, 2, 3]
    +
    +arr.length = 6;
    +arr.push(4);
    +arr; // [1, 2, 3, undefined, undefined, undefined, 4]
    +

    Assegnando una lunghezza più piccola si tronca l'array. Incrementandola si +crea un array frammentato.

    +

    In conclusione

    +

    Per la miglior performance, si raccomanda di usare sempre il ciclo for +classico e fare il caching della proprietà length. L'uso di for in su di +un array è segno di un codice scritto male che è suscettibile a bug e pessima +performance.

    +

    Il costruttore Array

    Dato che il costruttore Array è ambiguo riguardo a come esso gestisca i suoi +parametri, si consiglia calorosamente di usare l'array letterale (notazione []) +quando si creano array.

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

    Nei casi in cui c'è solo un argomento passato al costruttore Array e quando +l'argomento è un Number, il costruttore ritornerà un nuovo array frammentato +con la proprietà length impostata al valore dell'argomento. Si noti +che in questo modo solo la proprietà length del nuovo array verrà impostata, +mentre gli indici dell'array non verranno inizializzati.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, l'indice non è stato impostato
    +

    Essere in grado di impostare la lunghezza dell'array in anticipo è utile soltanto +in poche situazioni, come ad esempio la ripetizione di una stringa, nel cui caso +si eviterebbe l'uso di un ciclo.

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

    In conclusione

    +

    I letterali sono da preferirsi al costruttore Array. Sono più concisi, hanno una +sintassi più chiara ed incrementano la leggibilità del codice.

    +

    Tipi di dati

    Uguaglianza e comparazioni

    JavaScript usa due differenti metodi per comparare l'uguaglianza dei +valori degli oggetti.

    +

    L'operatore di uguaglianza

    +

    L'operatore di uguaglianza consiste di due segni di uguaglianza: ==.

    +

    JavaScript supporta la tipizzazione debole. Questo significa che +l'operatore di uguaglianza converte i tipi in modo da poterli +confrontare.

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

    Questa tabella mostra i risultati della conversione di tipo, ed è il +principale motivo per cui l'uso di == è ampiamente considerato una +cattiva pratica. Esso introduce bug difficili da rilevare a causa delle +complesse regole di conversione.

    +

    Inoltre, c'è anche un impatto sulla performance quando entra in gioco la +conversione di tipo. Ad esempio, una stringa deve essere convertita in un +numero prima di poter essere confrontata con un altro numero.

    +

    L'operatore di uguaglianza stretta

    +

    L'operatore di uguaglianza stretta consiste di tre segni di uguaglianza: ===.

    +

    Funziona come il normale operatore di uguaglianza, con l'eccezione di +non eseguire la conversione di tipo tra gli operandi.

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

    I risultati qui sono più chiari e permettono di identificare subito un problema +con il codice. Questo rende il codice più solido di un certo grado e fornisce anche +migliorie alla performance nel caso di operandi di tipo differente.

    +

    Comparazione di oggetti

    +

    Nonostante == e === vengano definiti operatori di uguaglianza, essi +funzionano differentemente quando almeno uno degli operandi è un Object.

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

    Qui, entrambe gli operatori confrontano per identità e non per +uguaglianza. Essi confrontano, cioè, che sia la stessa istanza dell'oggetto, +in modo molto simile a is in Python e la comparazione di puntatori in C.

    +

    In conclusione

    +

    Si raccomanda calorosamente di usare solo l'operatore di uguaglianza stretta. +Nei casi dove è necessario che i tipi vengano convertiti, questa operazione +dovrebbe essere fatta esplicitamente piuttosto che essere +lasciata alle complesse regole di conversione del linguaggio.

    +

    L'operatore typeof

    L'operatore typeof (assieme a instanceof) è +probabilmente il più grande difetto di progettazione di JavaScript, +dato che è quasi completamente inusabile.

    +

    Sebbene instanceof abbia ancora limitati casi d'uso, typeof ha realmente +un solo caso d'uso, che non è quello di verificare il tipo di un oggetto.

    + +

    Tabella dei tipi di JavaScript

    +
    Valore              Classe     Tipo
    +-------------------------------------
    +"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 questa tabella, Tipo fa riferimento al valore ritornato dall'operatore typeof. +Come si può chiaramente vedere, questo valore è tutto fuorchè affidabile.

    +

    Classe si riferisce al valore della proprietà interna [[Class]] di un oggetto.

    + +

    Per ottenere il valore di [[Class]], bisogna usare il metodo toString di +Object.prototype.

    +

    La classe di un oggetto

    +

    Le specifiche forniscono esattamente un modo per accedere al valore di +[[Class]], con l'uso di 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
    +

    Nel esempio qui sopra, Object.prototype.toString viene chiamato con il valore +di this impostato all'oggetto di cui si vuole ottenere il +valore di [[Class]].

    + +

    Testare variabili non definite

    +
    typeof foo !== 'undefined'
    +

    Questo esempio verificherà se foo è stata attualmente dichiarata oppure no. +Un semplice referenziamento ad essa risulterebbe in un ReferenceError. +Questo è l'unico caso in cui typeof è utile a qualcosa.

    +

    In conclusione

    +

    Per verificare il tipo di un oggetto, è altamente raccomandato l'utilizzo di +Object.prototype.toString, dato che questo è il solo modo affidabile per +fare ciò. Come mostrato nella tabella precedente, alcuni valori di ritorno +di typeof non sono definiti nelle specifiche, e ciò dimostra come essi +potrebbero differire tra implementazioni differenti.

    +

    A meno che non si debba verificare se una variabile è definta, typeof +dovrebbe essere evitato.

    +

    L'operatore instanceof

    L'operatore instanceof confronta i costruttori dei suoi due operandi. +È utile soltanto per la comparazione di oggetti realizzati dal +programmatore. Se usato sui tipi interni del linguaggio, esso è +praticamente inutile alla stregua dell'operatore typeof.

    +

    Confronto di oggetti personalizzati

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Questo imposta Bar.prototype all'oggetto funzione Foo,
    +// ma non ad un'istanza di Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    Uso di instanceof con i tipi nativi

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

    Un'importante cosa da notare qui è che instanceof non funziona con oggetti +originati da differenti contesti JavaScript (ad esempio, differenti +documenti in un browser web), dato che i loro costruttori non saranno +esattamente lo stesso oggetto.

    +

    In conclusione

    +

    L'operatore instanceof dovrebbe essere usato solo quando si ha a che fare +con oggetti personalizzati creati dal programmatore, che provengono dallo +stesso contesto JavaScript. Proprio come per l'operatore typeof, +ogni altro tipo di utilizzo dovrebbe essere evitato.

    +

    Conversione di tipo (Type Casting)

    JavaScript è un linguaggio debolmente tipizzato, perciò esso applicherà +una conversione di tipo ovunque sia possibile.

    +
    // Queste sono vere
    +new Number(10) == 10; // l'oggetto Number viene convertito
    +                      // in una primitiva numero tramite chiamata implicita
    +                      // al metodo Number.prototype.valueOf
    +
    +10 == '10';           // String viene convertita in Number
    +10 == '+10 ';         // Stringa più assurda
    +10 == '010';          // a ancora di più
    +isNaN(null) == false; // null viene convertito in 0
    +                      // che ovviamente non è NaN
    +
    +// Queste sono false
    +10 == 010;
    +10 == '-10';
    + +

    Per evitare i problemi appena visti, l'uso +dell'operatore di uguaglianza stretta è altamente +raccomandato. Sebbene questo eviti molti dei comuni problemi, ci sono ancora +molti ulteriori problemi che possono essere generati dal sistema debolmente +tipizzato di JavaScript.

    +

    Costruttori di tipi interni

    +

    I costruttori dei tipi interni del linguaggio, come Number e String, +funzionano in modo differente a seconda che venga usata o meno la +parola chiave new.

    +
    new Number(10) === 10;     // False, Object e Number
    +Number(10) === 10;         // True, Number e Number
    +new Number(10) + 0 === 10; // True, a causa della conversione implicita
    +

    L'uso di un tipo di dato interno come Number come costruttore, creerà un +nuovo oggetto Number, ma l'omissione della parola chiave new farà sì +che la funzione Number agisca da convertitore.

    +

    Inoltre, il passaggio di valori letterali o non oggetto risulterà in un'ancora +maggiore conversione di tipo.

    +

    La miglior opzione è quella di fare esplicitamente la conversione ad uno +dei tre possibili tipi.

    +

    Convertire in una stringa

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

    Anteponendo una stringa vuota, un valore può facilmente essere convertito in +una stringa.

    +

    Convertire in un numero

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

    Usando l'operatore unario di addizione, è possibile convertire in un numero.

    +

    Convertire in un booleano

    +

    Usando due volte l'operatore not, un valore può essere convertito in un +booleano.

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

    Base

    Perché non usare eval

    La funzione eval eseguirà una stringa di codice JavaScript nello scope locale.

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

    Comunque, eval esegue solo nello scope locale quando viene chiamata +direttamente e quando il nome della funzione chiamata è eval.

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

    L'uso di eval dovrebbe essere evitato. Il 99.9% dei suoi "utilizzi" può +essere ottenuto senza di essa.

    +

    eval sotto mentite spoglie

    +

    Le funzioni di timeout setTimeout e setInterval possono +entrambe accettare una stringa come loro primo argomento. Questa stringa verrà +sempre eseguita nello scope globale dato che eval non viene chiamato +direttamente in questo caso.

    +

    Problemi di sicurezza

    +

    eval è anche un problema di sicurezza, perché essa esegue qualsiasi +codice le viene passato. Non si dovrebbe mai usare con stringhe di origine +sconosciuta o inaffidabile.

    +

    In conclusione

    +

    eval non dovrebbe mai essere usata. Qualsiasi codice che ne faccia uso dovrebbe +essere messo in discussione sotto l'aspetto della funzionalità, della performance +e della sicurezza. Se qualcosa richiede eval per poter funzionare, allora non +dovrebbe essere usato in primo luogo, ma si dovrebbe prevedere una +miglior progettazione che non richieda l'uso di eval.

    +

    undefined e null

    JavaScript usa due valori distinti per il nulla, null e undefined, e +quest'ultimo è il più utile.

    +

    Il valore undefined

    +

    undefined è un tipo con esattamente un valore: undefined.

    +

    Il linguaggio definisce anche una variabile globale che ha il valore di undefined. +Questa variabile è anche chiamata undefined. Comunque, questa variabile non è +né una costante né una parola chiave del linguaggio. Ciò significa che il suo valore +può facilmente essere sovrascritto.

    + +

    Ecco alcuni esempi di quando il valore undefined viene ritornato:

    +
      +
    • Accedendo la variabile globale (non modificata) undefined.
    • +
    • Accedendo una variabile dichiarata ma non ancora inizializzata.
    • +
    • Ritorno implicito da funzioni che non hanno l'istruzione return.
    • +
    • Istruzioni return che non ritornano esplicitamente alcun valore.
    • +
    • Ricerca di proprietà inesistenti.
    • +
    • Parametri funzione a cui non viene esplicitamente passato alcun valore.
    • +
    • Qualsiasi cosa a cui sia stato assegnato il valore undefined.
    • +
    • Qualsiasi espressione nella forma di void(espressione).
    • +
    +

    Gestire le modifiche al valore di undefined

    +

    Dato che la variabile globale undefined mantiene solo una copia dell'attuale +valore di undefined, assegnandole un nuovo valore non cambia il valore del +tipo undefined.

    +

    Inoltre, per confrontare qualcosa con il valore di undefined, è necessario +ottenere prima il valore di undefined.

    +

    Per proteggere il codice da possibili sovrascritture della variabile undefined, +viene usata una comune tecnica che prevede l'aggiunta di un ulteriore parametro +ad un contenitore anonimo al quale non viene passato alcun +argomento.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // ora undefined nello scope locale
    +    // fa nuovamente riferimento al valore `undefined`
    +
    +})('Hello World', 42);
    +

    Un altro modo per ottenere lo stesso effetto sarebbe quello di usare una +dichiarazione all'interno del contenitore.

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

    La sola differenza è che questa versione si traduce in 4 byte in più quando +minificata, e non c'è nessun'altra istruzione var al'interno del contenitore +anonimo.

    +

    Utilizzi di null

    +

    Mentre undefined nel contesto del linguaggio JavaScript viene principalmente +usato come un tradizionale null, l'attuale null (sia letterale che tipo di +dati) è più o meno solo un altro tipo di dato.

    +

    Viene usato in alcune funzioni interne al JavaScript (come la dichiarazione +del termine della catena di prototipi, impostando Foo.prototype = null), ma +nella maggior parte dei casi, può essere rimpiazzato da undefined.

    +

    Inserimento automatico dei punti-e-virgola

    Sebbene JavaScript utilizzi lo stile di sintassi del C, esso non +obbliga l'uso dei punti-e-virgola nel codice sorgente, perciò è possibile +ometterli.

    +

    Detto questo, JavaScript non è un linguaggio che fa a meno dei punti-e-virgola. +Infatti, esso necessita di punti-e-virgola per poter comprendere il codice +sorgente. Quindi, il parser del JavaScript li inserisce automaticamente +ogni volta che incontra un errore di analisi dato dalla mancanza di un +punto-e-virgola.

    +
    var foo = function() {
    +} // errore di analisi, atteso punto-e-virgola
    +test()
    +

    Quindi avviene l'inserimento, ed il parser prova nuovamente.

    +
    var foo = function() {
    +}; // nessun errore, il parser continua
    +test()
    +

    L'inserimento automatico dei punti-e-virgola è considerato essere uno dei +più grandi errori di progettazione del linguaggio, perché può +modificare il comportamento del codice.

    +

    Come funziona

    +

    Il codice qui sotto non ha punti-e-virgola, quindi sta al parser decidere dove +inserirli.

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

    Di seguito il risultato del gioco da "indovino" del parser.

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Non inserito, linee unite
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- inserito
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- inserito
    +
    +        return; // <- inserito, invalida l'istruzione return
    +        { // trattato come un blocco
    +
    +            // un'etichetta e una singola espressione
    +            foo: function() {}
    +        }; // <- inserito
    +    }
    +    window.test = test; // <- inserito
    +
    +// Le linee vengono unite nuovamente
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- inserito
    +
    +})(window); //<- inserito
    + +

    Il parser ha drasticamente modificato il comportamento del codice. In alcuni casi, +questo porta ad eseguire cose sbagliate.

    +

    Parentesi ad inizio riga

    +

    Nel caso di parentesi ad inizio riga, il parser non inserirà un punto-e-virgola.

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

    Questo codice viene trasformato in una sola linea.

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

    Le possibilità che log non ritorni una funzione sono veramente alte, +perciò il codice qui sopra porterà ad un TypeError dichiarando che +undefined is not a function (undefined non è una funzione).

    +

    In conclusione

    +

    È fortemente raccomandato di non omettere mai i punti-e-virgola. +Si raccomanda anche di mantenere le parentesi sulla stessa linea della +corrispondente istruzione, e di non ometterle mai in istruzioni if / else +a linea singola. Queste misure precauzionali non solo miglioreranno la +consistenza del codice, ma preverranno anche che il parser JavaScript +modifichi il comportamento del codice in modo inaspettato.

    +

    L'operatore delete

    In breve, è impossibile eliminare variabili globali, funzioni e qualche +altra cosa in JavaScript che ha l'attributo DontDelete impostato.

    +

    Codice globale e codice funzione

    +

    Quando una variabile o una funzione viene definita in un scope globale o +funzione, essa è una proprietà dell'oggetto Activation +o dell'oggetto Global. Queste proprietà hanno un set di attributi, tra i quali +DontDelete. Dichiarazioni di variabile o funzione nel codice globale o +funzione, creano sempre proprietà con DontDelete, e quindi non possono essere +eliminate.

    +
    // variabile globale:
    +var a = 1; // DontDelete è impostato
    +delete a; // false
    +a; // 1
    +
    +// funzione normale:
    +function f() {} // DontDelete è impostato
    +delete f; // false
    +typeof f; // "function"
    +
    +// la riassegnazione non aiuta:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    Proprietà esplicite

    +

    Proprietà esplicitamente impostate possono essere eliminate normalmente.

    +
    // proprietà impostata esplicitamente:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    Nel codice qui sopra, obj.x e obj.y possono essere eliminate perché +non hanno l'attributo DontDelete. Ecco perché anche l'esempio seguente +funziona.

    +
    // questo funziona, tranne che per IE:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true - solo una variabile globale
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    Qui usiamo un trucco per eliminare a. this qui fa +riferimento all'oggetto Global e noi dichiariamo esplicitamente la +variabile a come sua proprietà, il che ci permette di eliminarla.

    +

    IE (almeno 6-8) ha alcuni bug, quindi il codice precedente non funziona.

    +

    Argomenti funzione e proprietà interne

    +

    Anche i normali argomenti delle funzioni, gli +oggetti arguments e le proprietà interne hanno +DontDelete impostato.

    +
    // argomenti funzione e proprietà:
    +(function (x) {
    +
    +  delete arguments; // false
    +  typeof arguments; // "object"
    +
    +  delete x; // false
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // false
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    Oggetti non nativi (host)

    +

    Il comportamento dell'operatore delete può essere inaspettato con gli oggetti +non nativi. A causa delle specifiche, agli oggetti non nativi è permesso di +implementare qualsiasi tipo di funzionalità.

    +

    In conclusione

    +

    L'operatore delete spesso ha un comportamento inaspettato e può solo essere +usato con sicurezza per eliminare proprietà esplicitamente impostate in oggetti +normali.

    +

    Varie

    setTimeout e setInterval

    Dato che JavaScript è asincrono, è possibile programmare l'esecuzione di una +funzione usando le funzioni setTimeout e setInterval.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // ritorna un Number > 0
    +

    Quando chiamato, setTimeout ritorna l'ID del timeout e programma foo per +essere eseguito approssimativamente un migliaio di millisecondi nel futuro. +foo verrà quindi eseguito una volta.

    +

    Dipendendo dalla risoluzione del timer del motore JavaScript che esegue il codice, +come anche dal fatto che JavaScript è single threaded e quindi altro codice +potrebbe essere eseguito bloccando il thread, non è mai sicuro scommettere +che una funzione verrà eseguita esattamente al ritardo specifiato nella chiamata +a setTimeout.

    +

    La funzione che è stata passata come primo parametro verrà chiamata dall'oggetto globale, +e ciò significa che this all'interno della funzione chiamata +farà riferimento all'oggetto globale.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this fa riferimento all'oggetto globale
    +        console.log(this.value); // stamperà undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Sovrapposizione di chiamate con setInterval

    +

    Mentre setTimeout esegue solo una volta la funzione, setInterval (come il +nome suggerisce) eseguirà la funzione ogni X millisecondi, ma il suo +utilizzo è sconsigliato.

    +

    Quando il codice che viene eseguito blocca la chiamata timeout, setInterval +eseguirà ancora più chiamate alla specifica funzione. Questo può, specialmente +con intervalli molto brevi, tradursi in chiamate a funzione che si sovrappongono.

    +
    function foo(){
    +    // qualcosa che blocca per 1 secondo
    +}
    +setInterval(foo, 1000);
    +

    Nel codice precedente, foo verrà chiamato una volta e quindi bloccherà per +un secondo.

    +

    Mentre foo blocca il codice, setInterval continuerà a programmare ulteriori +chiamate ad essa. Ora, quando foo ha finito, ci saranno già dieci ulteriori +chiamate ad essa in attesa per essere eseguite.

    +

    Gestione di potenziale codice bloccante

    +

    La soluzione più semplice, come anche la più controllabile, è quella di usare +setTimeout all'interno di se stessa.

    +
    function foo(){
    +    // qualcosa che blocca per 1 secondo
    +    setTimeout(foo, 1000);
    +}
    +foo();
    +

    Non solo questo incapsula la chiamata a setTimeout, ma previene anche la +sovrapposizione delle chiamate e da un controllo addizionale. foo stessa +può ora decidere se vuole continuare ad essere eseguita oppure no.

    +

    Pulizia manuale dei timeout

    +

    La pulizia di timeout ed intervalli funziona passando il rispettivo ID a +clearTimeout o clearInterval, in base a quale set di funzioni è stato +usato precedentemente.

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

    Pulizia di tutti i timeout

    +

    Dato che non c'è un metodo interno per la pulizia di tutti i timeout e/o +intervalli, è necessario usare la forza bruta per poter raggiungere questo +scopo.

    +
    // pulisce "tutti" i timeout
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Ma ci potrebbero ancora essere timeout che non vengono toccati da questo +numero arbitrario. Un altro modo per ottenere ciò, è considerare che l'ID +dato ad un timeout viene incrementato di uno ogni volta che si chiama +setTimeout.

    +
    // pulisce "tutti" i timeout
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    Sebbene questo funzioni con la maggior parte dei browser odierni, non è +specificato che gli ID debbano essere ordinati in quel modo e ciò potrebbe +anche cambiare in futuro. Perciò, si raccomanda di tener traccia di tutti +gli ID dei timeout, così che possano essere puliti in modo specifico.

    +

    Uso nascosto di eval

    +

    setTimeout e setInterval possono anche accettare una stringa come loro +primo parametro. Questa caratteristica non dovrebbe essere mai usata +perché internamente fa uso di eval.

    + +
    function foo() {
    +    // verrà chiamata
    +}
    +
    +function bar() {
    +    function foo() {
    +        // non verrà mai chiamata
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Dal momento che eval non viene chiamata direttamente in questo +caso, la stringa passata a setTimeout verrà eseguita nello scope globale. +Quindi, non verrà usata la variabile locale foo dallo scope di bar.

    +

    Si raccomanda inoltre di non usare una stringa per passare argomenti alla +funzione che verrà chiamata da una delle funzioni di timeout.

    +
    function foo(a, b, c) {}
    +
    +// non usare MAI questo
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// Usare invece una funzione anonima
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    In conclusione

    +

    Una stringa non dovrebbe mai essere usata come parametro di setTimeout o +setInterval. È un chiaro segno di codice veramente pessimo, quando +gli argomenti necessitano di essere passati alla funzione che deve essere +chiamata. Dovrebbe invece essere passata una funzione anonima che si incarichi +di gestire l'effettiva chiamata.

    +

    Inoltre, l'uso di setInterval dovrebbe essere evitato perché il suo schedulatore +non viene bloccato dall'esecuzione di JavaScript.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/ja/index.html b/external/JavaScript-Garden/ja/index.html new file mode 100644 index 0000000..9de7960 --- /dev/null +++ b/external/JavaScript-Garden/ja/index.html @@ -0,0 +1,1126 @@ +JavaScript Garden

    前書き

    前書き

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

    +

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

    +

    著者

    +

    このガイドは愛すべきStack Overflowの2人のユーザー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エンジンに正しく解釈させる為には文字リテラルを使って記述する事を推奨します。

    +

    プロトタイプ

    JavaScriptはクラスベース継承モデルは実装されておらず、この代わりにプロトタイプを用いています。

    +

    プロトタイプモデルを使っている事が、JavaScriptの弱点の一つになっていると良く考えられがちですが、プロトタイプ継承モデルはクラスベース継承モデルよりパワフルだというのは事実です。この事はちょっとしたものでもクラスベースの継承で実装しようとすると、プロトタイプベースの継承よりも作業が難しくなるという事でも分かります。

    +

    JavaScriptはプロトタイプベースが採用されている唯一の広範に使用されている基本的なプログラミング言語という現実があるので、プロトタイプベースとクラスベースの違いを時々調整しないとなりません。

    +

    最初の大きな違いはJavaScriptの継承はプロトタイプチェーンと呼ばれるもので実行されているという事です。

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// BarのプロトタイプをFooの新しいインスタンスとしてセットする
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Barを実際のコンストラクタとして確実にする為に代入する
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // 新しくbarインスタンスを作成
    +
    +// プロトタイプチェーンの結果
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* その他 */ }
    +

    上記ではtestBar.prototypeFoo.prototypeの2つのオブジェクトより継承されます。その為Fooの中で設定されたmethod関数にアクセスできるようになります。また、Fooのプロトタイプとしてのインスタンスそれ自体valueプロパティにもアクセスが可能です。new Bar()Fooのインスタンスを新しく作りませんが、プロトタイプに割り合てられたFooインスタンスを再利用している事は注目に値します。従って全てのBarインスタンスは同じvalueプロパティを共有します。

    + +

    プロパティ探索

    +

    オブジェクトのプロパティにアクセスする時には、JavaScriptはプロトタイプチェーンを要求された名前を見つけるまで遡って探索します。

    +

    チェーンの先頭(すなわちObject.prototype)に到達した際に、まだ指定されたプロパティが見つからなければ、代わりにundefinedという値を返します。

    +

    プロトタイププロパティ

    +

    プロトタイププロパティはJavaScriptの中でプロトタイプチェーンを構築する為に使われていますが、任意の値を代入する事も可能になっています。しかし、プロトタイプとしてプリミティブが代入された場合は単に無視されるだけです。

    +
    function Foo() {}
    +Foo.prototype = 1; // 効果無し
    +

    オブジェクトの代入は上記の例のように動作し、動的にプロトタイプチェーンを作る事ができます。

    +

    パフォーマンス

    +

    プロトタイプチェーンの上位にあるプロパティを探索する時間はコードの実行パフォーマンスに重大な悪影響を与えます。特に存在しないプロパティにアクセスしようとすると、プロトタイプチェーンの全てのプロパティを探索してしまいます。

    +

    また、オブジェクトのプロパティに対して反復処理をすると、プロトタイプチェーン上の全てのプロパティを列挙してしまいます。

    +

    既存のプロトタイプの拡張

    +

    元々組み込まれてるプロトタイプやObject.prototypeを拡張するのは、良くありがちなイケていない実装方法になります。

    +

    このテクニックはmonkey patchingと呼ばれるものでカプセル化を壊してしまいます。このテクニックはPrototypeのようなフレームワークにより広まりましたが、非標準の機能を持っている組み込み型のオブジェクトの乱立という点でも推奨されません。

    +

    唯一組み込みのプロトタイプを拡張しても良い理由としては、JavaScriptエンジンに将来実装されるであろう機能の移植だけです。 +例えばArray.forEachなどが、それに当たります。

    +

    終わりに

    +

    ここまでがプロトタイプベース継承モデルを使って複雑なコードを書く前に必ず理解すべき事です。また、プロパティチェーンの長さを観察して、もしパフォーマンスに悪影響を及ぼすのを防ぐ為ならば、これを分割をしなければなりません。さらに組み込みのプロトタイプは新しいJavaScriptの機能と互換性が無い限りは絶対に拡張してはいけません。

    +

    hasOwnProperty

    オブジェクトは自分自身自分以外のどちらで定義されたプロパティかをprototype chainのどこかでチェックしなくてはなりません。これはObject.prototypeから継承される全てのオブジェクトのhasOwnPropertyメソッドを使う必要があります。

    + +

    hasOwnPropertyはJavaScriptで唯一プロトタイプチェーン内を遡らずにプロパティを扱う事が出来ます。

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

    hasOwnPropertyだけが、正しく期待した結果を出すでしょう。これはあらゆるオブジェクトのプロパティの繰り返し処理をする時必須の事です。オブジェクト自身に定義されておらず、プロトタイプチェーンのどこかには定義されているというプロパティを除外する手段が他にありません

    +

    プロパティとしてのhasOwnProperty

    +

    JavaScriptはプロパティ名としてhasOwnPropertyを保護していません。;従って、この名前のプロパティを持ったオブジェクトが存在する事がありえます。正しい結果を得る為には外部hasOwnPropertyを使う必要があります。

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // 常にfalseを返す
    +
    +// 他のオブジェクトのhasOwnPropertyを使い、fooの'this'にセットして呼び出す
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    終わりに

    +

    オブジェクトのプロパティの存在判定をする時は、hasOwnProperty唯一のメソッドになります。 +また、全てfor in ループ内でhasOwnPropertyを使う事を推奨します。 +そうする事により組み込みのprototypesの拡張が原因のエラーを避ける事が出来ます。

    +

    for inループ

    inオペレーターは単に、for inループの中でオブジェクトのプロパティをプロトタイプチェーンの中で繰り返し遡る為にあるものです。

    + +
    // Object.prototype汚染
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // barとmooが両方とも表示される
    +}
    +

    for inループそれ自体の動作を変更する事は不可能ですが、ループ内にある要らないプロパティをフィルタリングする必要があります。そんな時はObject.prototypehasOwnPropertyメソッドを使うと解決します。

    + +

    hasOwnPropertyをフィルタリングに使用する

    +
    // 継承されているfoo
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    このループの唯一正しい使い方がこの方法です。hasOwnPropertyを使用しているので、 +mooのみが表示されるようになります。hasOwnPropertyが省略されている場合は、このコードは +組み込みのプロトタイプが存在する場合に(特にObject.prototypeが拡張されている場合)エラーを発生しやすくなります。

    +

    一般に広く使用されているJavaScriptフレームワークとしてPrototypeが挙げられます。このフレームワークには、 +for in 内でhasOwnPropertyが使用されプロトタプチェーン内を頭まで遡るのを中断する事が保証されています。

    +

    終わりに

    +

    常にhasOwnPropertyを使用する事を推奨します。コードの実行環境や、組み込みのプロトタイプが拡張されているかどうかを仮定して書くようなコードを絶対書いてはいけません。

    +

    関数

    関数の宣言と式

    関数はJavaScriptの第一級オブジェクトです。この事は、その他の値と同じように渡す事が出来るという事です。この機能で良く使われる一つとして匿名関数を他のオブジェクトにコールバックとして渡すというものがあり、これで非同期での実装が可能になります。

    +

    関数宣言

    +
    function foo() {}
    +

    上記の関数はプログラムの開始時の前に評価されるように巻き上げられます。従って定義されたスコープ内のどこでも使用する事が可能になります。ソース内での実際の定義が呼ばれる前でもです。

    +
    foo(); // このコードが動作する前にfooが作られているので、ちゃんと動作する
    +function foo() {}
    +

    関数

    +
    var foo = function() {};
    +

    この例では、fooという変数に無名で匿名の関数が割り当てられています。

    +
    foo; // 'undefined'
    +foo(); // これはTypeErrorが起こる
    +var foo = function() {};
    +

    varは宣言である為に、変数名fooがコードが開始される実際の評価時より前のタイミングにまで巻き上げられています。fooは既にスクリプトが評価される時には定義されているのです。

    +

    しかし、コードの実行時にのみこの割り当てがされるため、fooという変数は対応するコードが実行される前にデフォルト値であるundefinedが代入されるのです。

    +

    名前付き関数式

    +

    他に特殊なケースとして、名前付き関数があります。

    +
    var foo = function bar() {
    +    bar(); // 動作する
    +}
    +bar(); // ReferenceError
    +

    この場合のbarfooに対して関数を割り当てるだけなので、外部スコープでは使用できません。しかし、barは内部では使用できます。これはJavaScriptの名前解決の方法によるもので、関数名はいつも関数自身のローカルスコープ内で有効になっています。

    +

    thisはどのように動作するのか

    JavaScriptのthisと名付けられた特殊なキーワードは他のプログラム言語と違うコンセプトを持っています。JavaScriptのthisは正確に5個の別々の使い道が存在しています。

    +

    グローバルスコープとして

    +
    this;
    +

    thisをグローバルスコープ内で使用すると、単純にグローバルオブジェクトを参照するようになります。

    +

    関数呼び出しとして

    +
    foo();
    +

    このthisは、再度グローバルオブジェクトを参照しています。

    + +

    メソッド呼び出しとして

    +
    test.foo(); 
    +

    この例ではthistestを参照します。

    +

    コンストラクター呼び出し

    +
    new foo(); 
    +

    newキーワードが付いた関数呼び出しはコンストラクターとして機能します。関数内部ではthis新規に作成されたObjectを参照します。

    +

    thisの明示的な設定

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // 配列は下記で展開される
    +foo.call(bar, 1, 2, 3); // 結果はa = 1, b = 2, c = 3
    +

    Function.prototypecallapplyメソッドを使用した時には、呼び出された関数の内部でのthisの値は、対応する関数呼び出しの最初の引数に明示的に設定されます。

    +

    結果として、上記の例ではメソッドケースが適用されずfooの内部のthisbarに設定されます。

    + +

    良くある落し穴

    +

    これらのケースのほとんどは理にかなったものですが、最初のケースは実際に利用されることが絶対にないので、間違った言語設計だとみなせるでしょう。

    +
    Foo.method = function() {
    +    function test() {
    +        // このファンクションはグローバルオブジェクトに設定される
    +    }
    +    test();
    +};
    +

    良くある誤解としてtestの中のthisFooを参照しているというものがありますが、そのような事実は一切ありません。

    +

    testの中のFooにアクセスする為には、Fooを参照するmethodのローカル変数を作る必要があります。

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // ここでthisの代わりに使用する
    +    }
    +    test();
    +};
    +

    thatは通常の変数名ですが、外部のthisの参照の為に良く使われます。クロージャと組み合わせる事でthisの値を渡す事ができるようになります。

    +

    メソッドの割り当て

    +

    JavaScriptを使用する上で、もう一つ動かないものが関数のエイリアスです。これは変数へメソッドを割り当てする事です。

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

    最初のケースのtestは通常の関数呼び出しになる為に、この中のthisは、もはやsomeobjectを参照できなくなってしまいます。

    +

    thisの遅延バインディングは最初見た時にはダメなアイデアに見えますが、プロトタイプ継承により、きちんと動作します。

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

    methodBarのインスタンスにより呼び出された時に、thisはまさにそのインスタンスを参照するようになります。

    +

    クロージャと参照

    JavaScriptの一番パワフルな特徴の一つとしてクロージャが使える事が挙げられます。これはスコープが自身の定義されている外側のスコープにいつでもアクセスできるという事です。JavaScriptの唯一のスコープは関数スコープですが、全ての関数は標準でクロージャとして振る舞います。

    +

    プライベート変数をエミュレートする

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

    ここでCounter2つのクロージャを返します。関数incrementと同じく関数getです。これら両方の関数はCounterのスコープを参照し続けます。その為、そのスコープ内に定義されているcount変数に対していつもアクセスできるようになっています。

    +

    なぜプライベート変数が動作するのか?

    +

    JavaScriptでは、スコープ自体を参照・代入する事が出来無い為に、外部から変数countにアクセスする手段がありません。唯一の手段は、2つのクロージャを介してアクセスする方法だけです。

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

    上記のコードはCounterのスコープ中にある変数countの値を変更する事はありませんfoo.hackそのスコープで定義されていないからです。これは(Counter内の変数countの変更)の代わりにグローバル変数countの作成 -または上書き- する事になります。

    +

    ループ中のクロージャ

    +

    一つ良くある間違いとして、ループのインデックス変数をコピーしようとしてか、ループの中でクロージャを使用してしまうというものがあります。

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

    上記の例では0から9の数値が出力される事はありません。もっと簡単に10という数字が10回出力されるだけです。

    +

    匿名関数はiへの参照を維持しており、同時にforループは既にiの値に10をセットし終ったconsole.logが呼ばれてしまいます。

    +

    期待した動作をする為には、iの値のコピーを作る必要があります。

    +

    参照問題を回避するには

    +

    ループのインデックス変数をコピーする為には、匿名ラッパーを使うのがベストです。

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

    外部の匿名関数はiを即座に第一引数として呼び出し、引数eiのコピーとして受け取ります。

    +

    eを参照しているsetTimeoutを受け取った匿名関数はループによって値が変わる事がありません。

    +

    他にこのような事を実現する方法があります。それは匿名ラッパーから関数を返してあげる事です。これは上記のコードと同じ振る舞いをします。

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

    オブジェクトのarguments

    JavaScriptの全ての関数スコープはargumentsと呼ばれる特別な変数にアクセスできます。この変数は関数が受け取った全ての引数を保持する変数です。

    + +

    argumentsオブジェクトはArrayではありません。これは配列と同じような -lengthプロパティと名付けられています- 文法を持っていますが、Array.prototypeを継承している訳では無いので、実際Objectになります。

    +

    この為、argumentspushpopsliceといった通常の配列メソッドは使用する事が出来ません。プレーンなforループのような繰り返しでは上手く動作しますが、通常のArrayメソッドを使いたい場合は本当のArrayに変換しなければなりません。

    +

    配列への変換

    +

    下のコードはargumentsオブジェクトの全ての要素を含んだ新しいArrayを返します。

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

    この変換は遅いです。コードのパフォーマンスに関わる重要な部分での使用は推奨しません

    +

    引き数の受け渡し

    +

    下記の例はある関数から別の関数に引数を引き渡す際に推奨される方法です。

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

    他のテクニックとして、高速で非結合のラッパーとしてcallapply両方を一緒に使用するという物があります。

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// "メソッド"の非結合バージョンを作成する
    +// このメソッドはthis, arg1, arg2...argNのパラメーターを持っている
    +Foo.method = function() {
    +
    +    // 結果: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    仮パラメーターと引数のインデックス

    +

    argumentsオブジェクトはゲッターセッター機能を自身のプロパティと同様に関数の仮パラメーターとして作成します。

    +

    結果として、仮パラメーターを変更するとargumentsの対応する値も変更されますし、逆もしかりです。

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

    パフォーマンスの神話と真実

    +

    argumentsオブジェクトは、関数の内部の名前宣言と仮パラメーターという2つの例外を常に持ちながら生成されます。これは、使用されているかどうかは関係がありません。

    +

    ゲッターセッターは両方とも常に生成されます。その為これを使用してもパフォーマンスに影響は全くといって言い程ありません。argumentsオブジェクトのパラメーターに単純にアクセスしているような、実際のコードであれば尚更です。

    + +

    しかし、一つだけモダンJavaScriptエンジンにおいて劇的にパフォーマンスが低下するケースがあります。そのケースとはarguments.calleeを使用した場合です。

    +
    function foo() {
    +    arguments.callee; // この関数オブジェクトで何かする
    +    arguments.callee.caller; // そして関数オブジェクトを呼び出す
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // 通常はインライン展開する
    +    }
    +}
    +

    上記のコードでは、fooは自身と自身の呼び出し元の両方を知らないとインライン展開の対象になる事が出来ません。この事は、インライン展開によるパフォーマンスの向上の機会を失くす事になり、また、特定のコンテクストの呼び出しに依存する関数のせいで、カプセル化が解除されてしまいます。

    +

    この為にarguments.calleeを使用または、そのプロパティを決して使用しない事を強く推奨します。

    + +

    コンストラクタ

    JavaScriptのコンストラクタは色々ある他のプログラム言語とは一味違います。newキーワードが付いているどんな関数呼び出しも、コンストラクタとして機能します。

    +

    コンストラクタ内部では -呼び出された関数の事です- thisの値は新規に生成されたObjectを参照しています。この新規のオブジェクトのprototypeは、コンストラクタとして起動した関数オブジェクトのprototypeに設定されています。

    +

    もし呼び出された関数が、returnステートメントを明示していない場合は、暗黙の了解でthisの値を -新規のオブジェクトとして- 返します。

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

    上記は、Fooをコンストラクタとして呼び出し、新規に生成されたオブジェクトのprototypeFoo.prototypeに設定しています。

    +

    明示的にreturnステートメントがある場合、関数は返り値がObjectである場合に限りステートメントで明示した値を返します。

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // 新しいオブジェクト
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // 返ってきたオブジェクト
    +

    newキーワードが省略されている場合は、関数は新しいオブジェクトを返す事はありません

    +
    function Foo() {
    +    this.bla = 1; // グローバルオブジェクトに設定される
    +}
    +Foo(); // undefinedが返る
    +

    JavaScriptのthisの働きのせいで、上記の例ではいくつかのケースでは動作するように見える場合がありますが、それはグローバルオブジェクトthisの値として使用されるからです。

    +

    ファクトリー

    +

    newキーワードを省略するためには、コンストラクタ関数が明示的に値を返す必要があります。

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

    Barで呼び出されたものは両方とも全く同じものものになります。これには、methodと呼ばれるプロパティを持ったオブジェクトが新しく生成されますが、これはクロージャです。

    +

    また、注意する点として呼び出されたnew Bar()は返ってきたオブジェクトのプロトタイプに影響しません。プロトタイプは新しく生成されたオブジェクトにセットされはしますが、Barは絶対にその新しいオブジェクトを返さないのです。

    +

    上記の例では、newキーワードの使用の有無は機能的に違いがありません。

    +

    ファクトリーとして新しくオブジェクトを作成する

    +

    多くの場合に推奨される事として、newの付け忘れによるバグを引き起こしやすいので、newを使用しないようにするという事があります。

    +

    新しいオブジェクトを作成するためにファクトリーを使用して、そのファクトリー内部に新しいオブジェクトを作成すべきだという事です。

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

    上記の例ではnewキーワードが無いため堅牢になりますし、確実にプライベート変数を使用するのが簡単になりますが、いくつかの欠点があります。

    +
      +
    1. 作られたオブジェクトがプロトタイプ上のメソッドを共有しないために、よりメモリーを消費してしまいます。
    2. +
    3. ファクトリーを継承するために、他のオブジェクトの全てのメソッドをコピーする必要があるか、新しいオブジェクトのプロトタイプ上にそのオブジェクトを設置する必要があります。
    4. +
    5. newキーワードが無いという理由だけで、プロトタイプチェーンから外れてしまうのは、どことなく言語の精神に反します。
    6. +
    +

    終わりに

    +

    newキーワードが省略される事によりバグの可能性がもたらされますが、それによりプロトタイプを全く使わないという確かな理由にはなりません。最終的には、アプリケーションの必要性により、どちらの解決法がより良いかが決まってきます。特に大切なのは、オブジェクトの作成に特定のスタイルを選ぶ事、またそのスタイルに固執する事です。

    +

    スコープと名前空間

    JavaScriptはブロックに2つのペアの中括弧を使うのが素晴しいですが、これはブロックスコープをサポートしていません。その為、この言語に残されているのは関数スコープだけです。

    +
    function test() { // スコープ
    +    for(var i = 0; i < 10; i++) { // スコープではない
    +        // 数える
    +    }
    +    console.log(i); // 10
    +}
    + +

    JavaScriptはまた明確な名前空間を持ちません。この事は全て一つのグローバルで共有された名前空間で定義されるという事です。

    +

    変数が参照されるまでの間、JavaScriptはスコープ全てを遡って参照を探索します。グローバルスコープまで遡っても要求した名前が無いとReferenceErrorが発生します。

    +

    グローバル変数の致命傷

    +
    // スクリプト A
    +foo = '42';
    +
    +// スクリプト B
    +var foo = '42'
    +

    上記の2つのスクリプトは同じ効果を持っていません。スクリプト Aはfooと呼ばれる変数を、グローバルスコープに定義しており、スクリプト Bはfoo現在のスコープで定義ています。

    +

    繰り返しますが、この2つのスクリプトは同じ影響を全く持っていないスクリプトになります。varを使用しない事は重大な意味を持ちます。

    +
    // グローバルスコープ
    +var foo = 42;
    +function test() {
    +    // ローカルスコープ
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    test関数の中のvarステートメントを省略するとfooの値をオーバーライドします。最初の内は大した事ではないように思いますが、JavaScriptが何千行規模になると、varを使っていない事でバグの追跡が酷く困難になります。

    +
    // グローバルスコープ
    +var items = [/* 何かのリスト */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // サブループのスコープ
    +    for(i = 0; i < 10; i++) { // varステートメントが無くなった
    +        // 素敵な実装を!
    +    }
    +}
    +

    外側のループはsubloopが最初に呼ばれた後に終了します。なぜなら、subloopがグローバル変数iの値で上書きされているからです。2番目のforループにvarを使用する事によって簡単にこのエラーを回避する事ができます。目的とする効果を外側のスコープに与えようとしない限り、絶対varステートメントは省略してはいけません。

    +

    ローカル変数

    +

    JavaScriptのローカル変数の為の唯一の作成方法はfunctionパラメーターとvarステートメントによって宣言された変数になります。

    +
    // グローバルスコープ
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // 関数testのローカル変数
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    fooiは、関数testのスコープ内のローカル変数ですが、barの代入は同じ名前でグローバル変数で上書きしてしまいます。

    +

    巻き上げ

    +

    JavaScriptは宣言を巻き上げます。これはvarステートメントとfunction宣言が、それらを含むスコープの一番先頭に移動するという事を意味します。

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

    上記のコードは、実行を開始する前に変換されてしまいます。JavaScriptはvarステートメントと同じように、直近で囲んでいるfunction宣言を先頭に移動させます。

    +
    // varステートメントはここに移動する
    +var bar, someValue; // 'undefined'がデフォルト
    +
    +// function宣言もここに移動する
    +function test(data) {
    +    var goo, i, e; // 無くなったブロックスコープはこちらに移動する
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // barが'undefined'のままなので、Typeerrorで呼び出し失敗
    +someValue = 42; // 割り当てすると巻き上げの影響を受けない
    +bar = function() {};
    +
    +test();
    +

    ブロックスコープの欠落はvarステートメントをループやボディの外に移動するだけでなく、ifの構成を直感的ではないものにしてしまいます。

    +

    元のコードの中のifステートメントはグローバル変数であるgooも変更しているように見えますが、実際には -巻き上げが適用された後に- ローカル変数を変更しています。

    +

    巻き上げについての知識がないと、下に挙げたコードはReferenceErrorになるように見えます。

    +
    // SomeImportantThingが初期化されているかチェックする
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    しかし、勿論上記の動きはvarステートメントがグローバルスコープの上に移動しているという事実に基づいています。

    +
    var SomeImportantThing;
    +
    +// 他のコードがSomeImportantThingをここで初期化するかもしれないし、しないかもしれない
    +
    +// SomeImportantThingがある事を確認してください
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    名前解決の順序

    +

    JavaScriptのグローバルスコープを含む、全てのスコープは、現在のオブジェクトを参照している特殊な名前thisを持っています。

    +

    関数スコープはまた、argumentsという名前も持っています。それは関数スコープの中で定義され、関数に渡された引数を含んでいます。

    +

    例として、関数の中でfooと命名された変数にアクセスしようとする場合を考えましょう。JavaScriptは以下の順番で、その名前を探索しようとします。

    +
      +
    1. var fooステートメントが現在のスコープで使われている場合
    2. +
    3. fooという名前の関数パラメーターが存在するかどうか
    4. +
    5. 関数それ自体がfooとして呼ばれているかどうか
    6. +
    7. 一つ外のスコープに行き、再度#1から始める
    8. +
    + +

    名前空間

    +

    一つしかグローバルの名前空間を持たない事による良くある問題は変数名の衝突による問題の起きる可能性です。JavaScriptでは、この問題を匿名関数ラッパーの助けで簡単に回避できます。

    +
    (function() {
    +    // "名前空間"に自分を含む
    +
    +    window.foo = function() {
    +        // 露出したクロージャ
    +    };
    +
    +})(); // 即座に関数を実行する
    +

    無名関数はexpressionsとみなされ、呼び出し可能になり最初に評価されます。

    +
    ( // カッコ内の関数が評価される
    +function() {}
    +) // 関数オブジェクトが返される
    +() // 評価の結果が呼び出される
    +

    関数式を評価し、呼び出す別の方法として構文は違いますが、同様の動作をするのが下記です。

    +
    // 2つの別の方法
    ++function(){}();
    +(function(){}());
    +

    終わりに

    +

    自身の名前空間にカプセル化する為に常に匿名関数ラッパーを使用する事を推奨します。これは、コードを名前衝突から守る為だけでなく、プログラムのより良いモジュール化の為でもあります。

    +

    さらに、グローバル変数の使用は悪い習慣と考えられています。一回でもグローバル変数を使用するとエラーが発生しやすく、メンテナンスがしにくいコードになってしまいます。

    +

    配列

    配列の繰り返しとプロパティ

    JavaScriptの配列もまたオブジェクトですが、for in ループを配列の繰り返し処理で使用することの良い理由は1つもありません。実際、配列にfor inを使用しない為の正当な理由はたくさんあります。

    + +

    for inループはプロトタイプチェーン上の全てのプロパティを列挙するため、hasOwnPropertyをそれらのプロパティの存在判定に使います。この為、通常のforループよりも20倍遅くなります。

    +

    繰り返し

    +

    配列の要素を繰り返すとのに、最高のパフォーマンスを出したければ昔ながらのforループを使うのが一番です。

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

    上記の例では1つ追加の仕掛けがありますが、それはl = list.lengthによって配列の長さをキャッシュする部分です。

    +

    lengthプロパティは配列自身に定義されてはいますが、ループ中の繰り返しで毎回これを参照してしまうと、やはりオーバーヘッドが存在してしまいます。最近のJavaScriptエンジンはこのような場合に最適化するはずですが、コードが新しいエンジンで実行されるかどうか、知る方法はありません。

    +

    実際には、キャッシュを抜きにするとループの結果はキャッシュされたものに比べてたった半分の速度にしかなりません。

    +

    lengthプロパティ

    +

    lengthプロパティのゲッターは単に配列に含まれる要素の数を返すだけにも関わらず、セッターは配列をトランケートする為にも使用できます。

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

    より小さいlengthを割り当てると配列をトランケートしますが、lengthが大きくなっても配列には何も影響しません。

    +

    終わりに

    +

    最高のパフォーマンスの為には、常にforループを使用し、lengthプロパティをキャッシュする事をお勧めします。for inループを配列で使用するのは、バグや最低のパフォーマンスの傾向があるコードを書く前兆になります。

    +

    Arrayコンストラクター

    Arrayコンストラクターはそのパラメーターの扱い方が曖昧なので、新しい配列を作る時には、常に配列リテラル - []記法 - を使用する事を強くお勧めします。

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

    このケースの場合、Arrayコンストラクターに渡される引数は一つだけですが、その引数はNumberになります。コンストラクターは、引数に値がセットされたlengthプロパティを伴った新しい配列を返します。特筆すべきなのは、新しい配列のlengthプロパティのみが、このようにセットされるという事です。実際の配列のインデックスは初期化されません。

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, インデックスがセットされていない
    +

    配列の長さが先行してセットされるという振舞いは、いくつかの場合に便利です。例えば、文字の繰り返しや、for loopを使用したコードの回避などの場合です。

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

    終わりに

    +

    Arrayコンストラクターの使用は出来る限り避けてください。リテラルが当然望ましい形です。それらは、短かく明快な文法をもっている為に、コードの可読性を高めてくれます。

    +

    等価と比較

    JavaScriptはオブジェクトの値の等価の比較方法を2種類持っています。

    +

    等価演算子

    +

    等価演算子は2つのイコール記号: ==から成っています。

    +

    JavaScriptは弱い型付けを特徴としています。これは等価演算子が比較をする際に型付けを強制するという意味です。

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

    上記の表では型強制の結果が表示されています。==の使用が一般に悪い習慣とみなされる大きな理由として、変換ルールが複雑な為、バグの追跡が困難になる事が挙げられます。

    +

    加えて、型強制が行なわれるとパフォーマンスにも影響してしまいます。例えば、文字列は他の数字と比較する前に数値に変換されなければなりません。

    +

    厳密等価演算子

    +

    厳密等価演算子は3つのイコール記号:===で成っています。

    +

    これはオペランドの間で強制的な型変換が実行されない事を除けば、通常の等価演算子と同じように正確に動作します。

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

    上記の結果は、より明確でコードの早期破損を可能にします。これはある程度までコードを硬化させて、オペランドが別の型の場合にパフォーマンスが向上します。

    +

    オブジェクトの比較

    +

    =====は両方とも等価演算子とされていますが、そのオペランドの少なくとも一つがObjectの場合は、両者は異なる動きをします。

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

    これら2つの演算子は同一性を比較していているのであって、等価を比較しているわけではありません。これは、これらの演算子はPythonのis演算子やCのポインター比較と同じように、同じオブジェクトのインスタンスを比較するという事になります。

    +

    終わりに

    +

    厳密等価演算子だけを使用することを特に推奨します。型を強制的に型変換する場合はexplicitlyであるべきで、言語自体の複雑な変換ルールが残っているべきではありません。

    +

    typeof演算子

    typeof演算子(instanceofも同様です)は恐らくJavaScriptの最大の設計ミスです。完全に壊れている存在に近いものです。

    +

    instanceofはまだ限られた用途で使用できますが、typeofは本当に使用できる実用的なケースはオブジェクトの型を調べるという起こらないケース一つしかありません。

    + +

    JavaScript の型テーブル

    +
    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 (Nitro/V8ではfunction)
    +new RegExp("meow")  RegExp     object (Nitro/V8ではfunction)
    +{}                  Object     object
    +new Object()        Object     object
    +

    上記のテーブルにおいてTypetypeof演算子が返す値を参照しています。はっきりと分かるように、この値はどれでも一貫しています。

    +

    Classはオブジェクト内部の[[Class]]プロパティの値を参照しています。

    + +

    [[Class]]の値を取得する為に、Object.prototypeメソッドのtoStringを使う事があります。

    +

    オブジェクトのクラス

    +

    仕様では[[Class]]の値にアクセスするためには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
    +

    上記の例ではthisの値と共にObject.prototype.toStringが呼び出され[[Class]]の取得されている値がオブジェクトとして設定されます。

    + +

    未定義変数のテスト

    +
    typeof foo !== 'undefined'
    +

    上記ではfooが実際に宣言されたかどうかをReferenceErrorの結果を参照してチェックします。これはtypeofが唯一実際に役に立つ場合です。

    +

    終わりに

    +

    オブジェクトの型をチェックする為には、Object.prototype.toStringを使用する事を強くお勧めします。これが唯一信頼できる方法だからです。上述の型テーブルでも分かるように、typeofの戻り値は仕様で定義されていないものを返します。よって、実装によって別の結果になる事があります。

    +

    変数が定義されているかチェックしない限りは、typeofどんな事をしても避けるべきです。

    +

    instanceofオペレーター

    instanceofオペレーターは2つのオペランドのコンストラクタを比較します。これはカスタムで作ったオブジェクトを比較する時にのみ有用です。組み込みの型に使用するのはtypeof operatorを使用するのと同じくらい意味がありません。

    +

    カスタムオブジェクトの比較

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// これは単に関数オブジェクトFooにBar.prototypeをセットしただけです。
    +// しかし、実際のFooのインスタンスではありません。
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    ネイティブ型でinstanceofを使用する

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

    ここで1つ重要な事は、異なるJavaScriptのコンテキスト(例えば、ブラウザの異なるウィンドウ)を元としたオブジェクトでは、コンストラクタが厳密に同じものでは無い為にinstanceofは上手く動作しません。

    +

    終わりに

    +

    instanceofオペレーターは同じJavaScriptのコンテキストが起源になっているカスタムメイドのオブジェクトを扱う場合のみ使うべきです。ちょうどtypeofオペレーターのように、その他での使用は避けるべきです。

    +

    型変換

    JavaScriptは弱い型付けの言語なので、可能な限り型強制が適用されます。

    +
    // これらはtrueです。
    +new Number(10) == 10; // Number.toString()が変換される
    +                      // numberに戻る
    +
    +10 == '10';           // StringsがNumberに変換される
    +10 == '+10 ';         // バカみたいに文字列を追加
    +10 == '010';          // もっともっと
    +isNaN(null) == false; // nullが0に変換される
    +                      // もちろんNaNではないです
    +
    +// これらはfalseです
    +10 == 010;
    +10 == '-10';
    + +

    上記の自体を避ける為に、厳密等価演算子を使用する事を強く推奨します。また、これはたくさんある落し穴を避けますが、それでもまだJavaScriptの弱い型付けシステムから発生する色々な課題が残っています。

    +

    組み込み型のコンストラクタ

    +

    NumberStringのような組み込み型のコンストラクタは、newキーワードの有無で振る舞いが違ってきます。

    +
    new Number(10) === 10;     // False, ObjectとNumber
    +Number(10) === 10;         // True, NumberとNumber
    +new Number(10) + 0 === 10; // True, 暗黙の型変換によります
    +

    Numberのような組み込み型をコンストラクタとして使うと、新しいNumberオブジェクトが作られますが、newキーワードを除外するとNumber関数がコンバーターのように振る舞います。

    +

    加えて、リテラルかオブジェトではない値を持っていると、さらに型強制が多くなります。

    +

    最良のオプションは以下の3つの方法の内、1つで型を明示してキャストする事になります。

    +

    Stringでキャストする

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

    空の文字列の付加により値を簡単に文字列にキャストできます。

    +

    Numberでキャストする

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

    単項プラスオペレーターを使うと数字にキャストする事が可能です。

    +

    Booleanでキャストする

    +

    notオペレーターを2回使うと、値はブーリアンに変換できます。

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

    コア

    なぜ、evalを使ってはいけないのか

    eval関数はローカルスコープ中のJavaScriptコードの文字列を実行します。

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

    しかし、eval直接ローカルスコープから呼ばれて、かつ呼んだ関数の名前が実際のevalでないと実行しません。

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

    evalの使用は全てのコストを払ってでも回避するべきです。その「使用法」の99.9%で、これ無しでも実装できます。

    +

    偽装されたeval

    +

    timeout functionsであるsetTimeoutsetIntervalはどちらも最初の引数として文字列を取る事ができます。この文字列はevalがこの場合直接呼ばれていないので、常にグローバルスコープで実行されてしまいます。

    +

    セキュリティの問題

    +

    evalはまたセキュリティの問題もあります。なぜなら、どんなコードを与えられても実行してしまうからで、絶対に不明または信頼できない発行元の文字列は使ってはいけません。

    +

    終わりに

    +

    evalは絶対に使用しないでください。これを使用しているどんなコードも、その働き、パフォーマンスやセキュリティについて問われてしまいます。evalが必要な場合でも、最初の段階で使用しないでください。より良いデザインを使用するべきで、それにはevalを使う必要性はありません。

    +

    undefinednull

    JavaScriptはnothingを表す2つの別個の値を持っています。これら2つの内でundefinedはより便利な存在です。

    +

    undefinedの値

    +

    undefinedはただ1つの値undefinedを持つ型です。

    +

    この言語はまた、undefinedの値を持つグローバル変数を定義しています。この値もまたundefinedと呼ばれています。しかし、この変数は どちらも 言語のキーワードではなく、定数です。この事はこのは簡単に上書きされてしまうという事になります。

    + +

    undefinedが返される時の例をいくつか挙げます。

    +
      +
    • (未定義の)グローバル変数undefinedにアクセスした時
    • +
    • return文が無い為に、暗黙のうちに関数が返された時
    • +
    • 何も返されないreturnがある時
    • +
    • 存在しないプロパティを探索する時
    • +
    • 関数のパラメーターで明示的な値が何も無い時
    • +
    • undefinedが設定された全ての値
    • +
    +

    undefinedの値に変更する処理

    +

    グローバル変数undefinedのみが実際のundefinedのコピーを保持するので、これに新しい値を代入してもundefined の値が変更される事はありません

    +

    まだ、undefinedの値に対して何かしらの比較をしないといけない場合は、最初にundefinedの値を取得する必要があります。

    +

    コードのundefinedの変数の上書きを可能な限りしないよう保護する為には、一般的なテクニックとしてanonymous wrapperの引数にパラメーターを追加するというものがあります。

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // ローカルスコープではundefined。
    +    // ここで値に対して参照がされる
    +
    +})('Hello World', 42);
    +

    同じ効果を得る事ができる別の方法として、ラッパーの内部での宣言を使うものがあります。

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

    これらの唯一の違いは、こちらのバージョンの方が4バイト余計に短縮できるという物です。また、他にvarステートメントは匿名ラッパーの中にはありません。

    +

    nullの使用

    +

    JavaScriptというプログラム言語のコンテキストの中では、undefinedは主に伝統的な意味でのnullの意味で使用される事が多いです。実際のnull(リテラルも型も両方)は多かれ少なかれ、単なるデータ型です。

    +

    それはJavaScriptの内部でいくつか使われています(プロトタイプチェーンの終わりにFoo.prototype = nullという宣言をするようなもの)が、ほとんど全てのケースで、undefinedに置き替える事が可能です。

    +

    セミコロン自動挿入

    JavaScriptはC言語スタイルのシンタックスを持っていますが、これはソースコードの中でセミコロンの使用を強制している事にはならないので、これらを省略する事も可能です。

    +

    JavaScriptはセミコロン無しの言語ではありません。実際に、ソースコードを理解する為にもセミコロンは必要になります。ですので、JavaScriptのパーサーはセミコロンが無い事によるパースエラーを検出する度に、自動的にセミコロンを挿入します。

    +
    var foo = function() {
    +} // セミコロンが入っている事が期待されるので、パースエラーになる
    +test()
    +

    挿入が起こると、パーサーはもう一度パースします。

    +
    var foo = function() {
    +}; // エラーが無いので、パーサーは次の解析をする
    +test()
    +

    セミコロンの自動挿入は、コードの振る舞いを変えられる為に、言語の最大の欠陥の内の一つと考えられています。

    +

    どのように動くか

    +

    以下のコードはセミコロンが無いので、パーサーはどこにセミコロンを挿入するか決めなくてはなりません。

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

    下記がパーサーの「推測」ゲームの結果になります。

    +
    (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; // <- inserted, breaks the return statement
    +        { // ブロックとして扱われる
    +
    +            // a label and a single expression statement
    +            foo: function() {} 
    +        }; // <- 挿入
    +    }
    +    window.test = test; // <- 挿入
    +
    +// 再度行がマージされる
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- 挿入
    +
    +})(window); //<- 挿入
    + +

    パーサーは上記のコードの振舞いを劇的に変化させます。あるケースにおいては、間違っている事にもなってしまいます。

    +

    先頭の括弧

    +

    先頭に括弧がある場合、パーサーはセミコロンを挿入しません

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

    このコードは1つの行に変形します。

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

    logが関数を返さない確率はとても高いです。しかし、上記ではundefined is not a functionというTypeErrorが繰り返されます。

    +

    終わりに

    +

    セミコロンを省略するのは絶対にお勧めしません。括弧を対応する文と同じ行に記述すること、および一行のif / else文に対して括弧を省略しないことが推奨されています。これら両方の処理がコードの整合性を高めてくれるだけでなく、JavaScriptパーサーの振舞いを変えてしまうのを防いでくれるでしょう。

    +

    delete演算子

    端的に言って、JavaScriptの関数やその他の要素はDontDelete属性が設定されているので、グローバル変数を消去する事は不可能です。

    +

    グローバルコードと関数コード

    +

    変数や、関数がグローバルまたは関数スコープで定義された時は、そのプロパティは有効なオブジェクトかグローバルオブジェクトになります。このようなプロパティは属性のセットを持っていますが、それらの内の1つがDontDeleteになります。変数や関数がグローバルや関数コードで宣言されると、常にDontDelete属性を作るために、消去できません。

    +
    // グローバル変数:
    +var a = 1; // DontDelete属性が設定される
    +delete a; // false
    +a; // 1
    +
    +// 通常関数:
    +function f() {} // DontDelete属性が設定される
    +delete f; // false
    +typeof f; // "function"
    +
    +// 再代入も役に立たない:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    明示的なプロパティ

    +

    明示的にプロパティを設定することが、通常通りの消去を可能にします。

    +
    // プロパティを明示的に設定する
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    上記の例の中で、obj.xobj.yはそれぞれDontDelete属性が無い為に消去できます。これが下記の例でも動作する理由です。

    +
    // IE以外では、これも動作する
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true - ただのグローバルのvar
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    ここではa. thisを消す為にグローバルオブジェクトと明示的に宣言したaをそのプロパティとして参照させて、消去する事を許可するトリックを使います。

    +

    IE(最低でも6-8で)は多少のバグがある為に、上記のコードは動作しません。

    +

    関数の引数と組み込み引数

    +

    関数の通常の引数である、arguments objectsと組み込みのプロパティもまた、DontDeleteが設定されています。

    +
    // 関数の引数とプロパティ:
    +(function (x) {
    +
    +  delete arguments; // false
    +  typeof arguments; // "object"
    +
    +  delete x; // false
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // false
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    ホストオブジェクト

    +

    delete演算子の挙動はホストオブジェクトにとって予測不可能になりかねません。仕様によりホストオブジェクトは、あらゆる挙動の実行が許可されている為です。

    +

    終わりに

    +

    delete演算子は、しばしば予期せぬ挙動をします。唯一安全な使用方法は通常のオブジェクトに明示的に設定されたプロパティを扱う場合だけです。

    +

    その他

    setTimeoutsetInterval

    JavaScriptは非同期なので、setTimeoutsetInterval関数を使ってある関数の実行のスケジュールを決める事が可能です。

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // Number > 0を返す
    +

    setTimeoutが呼ばれた時に、タイムアウトのIDを返し、この先おおよそ1000ms以内に実行するfooをスケジュールします。fooは正確に1度だけ実行されます。

    +

    これは、setTimeout関数の呼び出しで指定した遅延時間を正確に間違いなく得られるという事では決してありません。コードが実行されているJavaScriptエンジンのタイマー分解能によって決まります。この事実はJavaScriptがシングルスレッドなので、他のスレッドでの実行を妨害してしまう事があるかもしれません。

    +

    第一パラメーターを渡された関数はグローバルオブジェクトによって呼び出されます。これは呼び出された関数の内部でthisがまさにこのオブジェクトを参照しているという事になります。

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // これはグローバルオブジェクトを参照しています
    +        console.log(this.value); // undefinedを記録するはずです
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    setIntervalでスタッキングコール

    +

    setTimeoutは関数を一度だけ実行します。setInterval - 名前が示すように - 毎回Xミリ秒毎に関数を実行しますが、この使用は推奨されていません。

    +

    コードがタイムアウト呼び出しブロックで実行される時に、setIntervalは指定された関数を呼び出します。これは、特に小さい間隔で、関数の結果をスタックに積む事ができます。

    +
    function foo(){
    +    // 1秒おきにブロックの何かを実行
    +}
    +setInterval(foo, 100);
    +

    上記のコードでは、fooが1回呼び出されて、1秒ブロックされます。

    +

    fooがコードをブロックしている間、setIntervalは呼び出される予定を確保しています。fooが完了した瞬間に、実行を待っている呼び出しが10回以上存在しているでしょう。

    +

    ブロッキング可能なコードの取り扱い

    +

    簡単かつ、一番コントロール可能な解決法として、関数自体の中でsetTimeoutを使うという方法があります。

    +
    function foo(){
    +    // 1秒ブロックする何か
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    このカプセル化はsetTimeoutの呼び出しだけでなく、呼び出しのスタッキングを防止してより詳細なコントロールが出来ます。fooそれ自身が今や、再度実行するかしないかを決める事が出来るのです。

    +

    手動でタイムアウトをクリアする

    +

    タイムアウトとインターバルのクリアは、clearTimeoutclearIntervalに個別のIDを渡せば出来ます。最初にset関数を使った場所に依存します。

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

    全てのタイムアウトをクリアする

    +

    全てのタイムアウトや、インターバルをクリアする組み込みメソッドが無い為、機能的にクリアする為には暴力的な手段を使う必要があります。

    +
    // "全ての"タイムアウトをクリアする
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    ここまでもまだ、任意の数字を与えられた為に影響を受けないタイムアウトがあるかもしれません。そのため、代わりに全てのタイムアウトのIDを追跡する事が推奨されます。それで個別にクリアされます。

    +

    隠されたevalの使用

    +

    setTimeoutsetInterval は、第一引数に文字列を取る事が可能です。この仕様は内部でevalを使用する為に、絶対に使うべきではありません。

    + +
    function foo() {
    +    // この先呼ばれる
    +}
    +
    +function bar() {
    +    function foo() {
    +        // 絶対に呼ばれない
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    この場合、eval直接呼ばれないので、文字列が渡されたsetTimeoutglobal scopeで実行されます。よって、barのスコープからfooのローカル変数は使われないのです。

    +

    いずれかのタイムアウト関数によって呼び出される関数に引数を渡すために文字列を使わないという事は、さらに推奨されています。

    +
    function foo(a, b, c) {}
    +
    +// 絶対にこのように使わない
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// 匿名関数を代わりに使用する
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    終りに

    +

    setTimeoutsetIntervalのパラメーターに文字列を用いてはいけません。引数が関数に呼び出される必要がある場合本当に悪いコードの明確なサインになります。実際の呼び出しには匿名関数を渡すべきです。

    +

    さらに、setIntervalの使用はスケジューラーがJavaScriptの実行によってブロックされないので、避けるべきでしょう。

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/javascript/garden.js b/external/JavaScript-Garden/javascript/garden.js new file mode 100644 index 0000000..3939a2c --- /dev/null +++ b/external/JavaScript-Garden/javascript/garden.js @@ -0,0 +1,207 @@ +// A thingy to handle the sections ---------------------------------------------- +// ------------------------------------------------------------------------------ +function Sections(page) { + this.page = page; + this.init(); +} + +Sections.prototype = { + init: function(attribute) { + this.heights = this.page.nav.find('ul').map(function(idx, ele) { + return $(this).outerHeight(); + }).get(); + + this.links = { + next: $('#nav_next_section'), + prev: $('#nav_prev_section') + }; + }, + + map: function() { + this.names = $('section>[id]').map(function(idx, ele) { + return { + id: this.id.replace('.intro', ''), + offset: $(this).offset().top - 100, + title: $(this).find(':header:first').html() + }; + + }).get(); + }, + + highlight: function() { + var scroll = this.page.window.scrollTop(), + articleID = this.names[this.names.length - 1].id; + + for(var i = 0, l = this.names.length; i < l; i++) { + if (scroll >= 0 && this.names[i].offset > scroll) { + articleID = this.names[i - 1].id; + break; + } + } + + var sectionID = articleID.split('.')[0], + page = this.page, + nav = page.nav; + + if (sectionID !== page.section) { + nav.filter('.nav_' + page.section).removeClass('active'); + nav.filter('.nav_' + sectionID).addClass('active'); + + this.expand(sectionID); + page.section = sectionID; + } + + if (articleID !== page.article) { + nav.find('a[href="#' + page.article + '"]').removeClass('active'); + nav.find('a[href="#' + articleID + '"]').addClass('active'); + + page.article = articleID; + this.mobile(articleID); + } + }, + + expand: function(sectionName) { + var nav = this.page.nav, + index = nav.find('a[href=#' + sectionName + ']') + .closest('nav > ul > li').index(); + + var height = this.page.window.height() + - $('nav > div').height() + - (33 * this.heights.length); + + var sections = [], + currentHeight = 0, + distance = 0; + + while ((currentHeight + this.heights[index]) < height) { + sections.push(index); + currentHeight += this.heights[index]; + + distance = -distance + (distance >= 0 ? -1 : 1); + index += distance; + + if (index < 0 || index >= this.heights.length) { + distance = -distance + (distance >= 0 ? -1 : 1); + index += distance; + } + } + + for(var i = 0, len = nav.length; i < len; i++) { + if ($.inArray(i, sections) === -1) { + nav.eq(i).find('ul').slideUp(); + + } else { + nav.eq(i).find('ul').slideDown(); + } + } + }, + + mobile: function(index){ + for(var i = 0; i < this.names.length; i++) { + if (this.names[i].id === index) { + this.updateLinks(i); + break; + } + } + }, + + updateLinks: function(index) { + if (index !== this.names.length - 1) { + this.setLink(this.links.next, this.names[index + 1]); + } else { + this.links.next.slideUp(100); + } + + if (index !== 0) { + this.setLink(this.links.prev, this.names[index - 1]); + } else { + this.links.prev.slideUp(100); + } + }, + + setLink: function(ele, data) { + ele.slideDown(100).attr('href', '#' + data.id) + .find('.nav_section_name').html(data.title); + } +}; + + +// This more or less controls the page ------------------------------------------ +// ------------------------------------------------------------------------------ +function Page() { + $.extend(true, this, { + window: $(window), + nav: $('nav > ul > li'), + section: null, + articule: null + }); + + this.sections = new Sections(this); + this.init(); +} + +Page.prototype = { + init: function() { + var that = this, + $mainNav = $('#nav_main'); + + $.extend(this, { + scrollLast: 0, + resizeTimeout: null + }); + + this.window.scroll(function() { + that.onScroll(); + }); + + this.window.resize(function() { + that.onResize(); + }); + + this.sections.map(); + setTimeout(function() { + that.sections.highlight(); + }, 10); + + // Show menu for tablets and smart phones + $('#show_menu').click(function() { + $mainNav.slideDown(300); + return false; + }); + + $mainNav.click(function() { + if(that.window.width() < 1000) + $mainNav.slideUp(300, function() { $mainNav.removeAttr('style'); }); + }); + }, + + onScroll: function() { + if ((+new Date()) - this.scrollLast > 50) { + this.scrollLast = +new Date(); + this.sections.highlight(); + } + }, + + onResize: function() { + clearTimeout(this.resizeTimeout); + + var that = this; + this.resizeTimeout = setTimeout(function() { + that.sections.map(); + that.sections.expand(that.section); + }, 100); + } +}; + +var Garden = new Page(); +prettyPrint(); + +// GA tracking code +var _gaq = _gaq || []; +_gaq.push(['_setAccount', 'UA-20768522-1'], ['_trackPageview']); +(function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); +})(); + diff --git a/external/JavaScript-Garden/javascript/html5.js b/external/JavaScript-Garden/javascript/html5.js new file mode 100644 index 0000000..3587bf7 --- /dev/null +++ b/external/JavaScript-Garden/javascript/html5.js @@ -0,0 +1,6 @@ +// html5shiv MIT @rem remysharp.com/html5-enabling-script +// iepp v1.6.2 MIT @jon_neal iecss.com/print-protector +/*@cc_on(function(m,c){var z="abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video";function n(d){for(var a=-1;++ai";if(g.childNodes.length!==1){var i=z.split("|"),o=i.length,s=RegExp("(^|\\s)("+z+")", +"gi"),t=RegExp("<(/*)("+z+")","gi"),u=RegExp("(^|[^\\n]*?\\s)("+z+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),r=c.createDocumentFragment(),k=c.documentElement;g=k.firstChild;var h=c.createElement("body"),l=c.createElement("style"),f;n(c);n(r);g.insertBefore(l, +g.firstChild);l.media="print";m.attachEvent("onbeforeprint",function(){var d=-1,a=p(c.styleSheets,"all"),e=[],b;for(f=f||c.body;(b=u.exec(a))!=null;)e.push((b[1]+b[2]+b[3]).replace(s,"$1.iepp_$2")+b[4]);for(l.styleSheet.cssText=e.join("\n");++d","/","/=",":","::",";","<","<<","<<=","<=","=","==","===",">",">=",">>",">>=",">>>",">>>=","?","@","[","^","^=","^^","^^=","{","|","|=","||","||=","~","break","case","continue","delete","do","else","finally","instanceof","return","throw","try","typeof"];var b='(?:^^|[+-]';for(var i=0;i:&a-z])/g,'\\$1')}b+=')\\s*';return b}();var ba=/&/g;var bb=//g;var bd=/\"/g;function attribToHtml(a){return a.replace(ba,'&').replace(bb,'<').replace(bc,'>').replace(bd,'"')}function textToHtml(a){return a.replace(ba,'&').replace(bb,'<').replace(bc,'>')}var be=/</g;var bf=/>/g;var bg=/'/g;var bh=/"/g;var bi=/&/g;var bj=/ /g;function htmlToText(a){var b=a.indexOf('&');if(b<0){return a}for(--b;(b=a.indexOf('&#',b+1))>=0;){var c=a.indexOf(';',b);if(c>=0){var d=a.substring(b+3,c);var e=10;if(d&&d.charAt(0)==='x'){d=d.substring(1);e=16}var f=parseInt(d,e);if(!isNaN(f)){a=(a.substring(0,b)+String.fromCharCode(f)+a.substring(c+1))}}}return a.replace(be,'<').replace(bf,'>').replace(bg,"'").replace(bh,'"').replace(bj,' ').replace(bi,'&')}function isRawContent(a){return'XMP'===a.tagName}var bk=/[\r\n]/g;function isPreformatted(a,b){if('PRE'===a.tagName){return true}if(!bk.test(b)){return true}var c='';if(a.currentStyle){c=a.currentStyle.whiteSpace}else if(window.getComputedStyle){c=window.getComputedStyle(a,null).whiteSpace}return!c||c==='pre'}function normalizedHtml(c,d,e){switch(c.nodeType){case 1:var f=c.tagName.toLowerCase();d.push('<',f);var g=c.attributes;var n=g.length;if(n){if(e){var h=[];for(var i=n;--i>=0;){h[i]=g[i]}h.sort(function(a,b){return(a.name');for(var k=c.firstChild;k;k=k.nextSibling){normalizedHtml(k,d,e)}if(c.firstChild||!/^(?:br|link|img)$/.test(f)){d.push('<\/',f,'>')}break;case 3:case 4:d.push(textToHtml(c.nodeValue));break}}function combinePrefixPatterns(q){var r=0;var s=false;var t=false;for(var i=0,n=q.length;i122)){if(!(j<65||h>90)){f.push([Math.max(65,h)|32,Math.min(j,90)|32])}if(!(j<97||h>122)){f.push([Math.max(97,h)&~32,Math.min(j,122)&~32])}}}f.sort(function(a,b){return(a[0]-b[0])||(b[1]-a[1])});var k=[];var l=[NaN,NaN];for(var i=0;im[0]){if(m[1]+1>m[0]){o.push('-')}o.push(encodeEscape(m[1]))}}o.push(']');return o.join('')}function allowAnywhereFoldCaseAndRenumberGroups(c){var d=c.source.match(new RegExp('(?:'+'\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'+'|\\\\u[A-Fa-f0-9]{4}'+'|\\\\x[A-Fa-f0-9]{2}'+'|\\\\[0-9]+'+'|\\\\[^ux0-9]'+'|\\(\\?[:!=]'+'|[\\(\\)\\^]'+'|[^\\x5B\\x5C\\(\\)\\^]+'+')','g'));var n=d.length;var e=[];for(var i=0,groupIndex=0;i=2&&g==='['){d[i]=caseFoldCharset(p)}else if(g!=='\\'){d[i]=p.replace(/[a-zA-Z]/g,function(a){var b=a.charCodeAt(0);return'['+String.fromCharCode(b&~32,b|32)+']'})}}}return d.join('')}var v=[];for(var i=0,n=q.length;i\n'));bl=!/)[\r\n]+/g,'$1').replace(/(?:[\r\n]+[ \t]*)+/g,' ')}return c}var d=[];for(var e=a.firstChild;e;e=e.nextSibling){normalizedHtml(e,d)}return d.join('')}function makeTabExpander(f){var g=' ';var h=0;return function(a){var b=null;var c=0;for(var i=0,n=a.length;i=0;e-=g.length){b.push(g.substring(0,e))}c=i+1;break;case'\n':h=0;break;default:++h}}if(!b){return a}b.push(a.substring(c));return b.join('')}}var bm=new RegExp('[^<]+'+'|<\!--[\\s\\S]*?--\>'+'|'+'|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'+'|<','g');var bn=/^<\!--/;var bo=/^1&&e.charAt(0)==='<'){if(bn.test(e)){continue}if(bo.test(e)){b.push(e.substring(9,e.length-3));c+=e.length-12}else if(bp.test(e)){b.push('\n');++c}else{if(e.indexOf(Y)>=0&&isNoCodeTag(e)){var f=e.match(bq)[2];var g=1;var j;end_tag_loop:for(j=i+1;j=0;){u[f.charAt(c)]=e}}var g=e[1];var k=''+g;if(!d.hasOwnProperty(k)){b.push(g);d[k]=null}}b.push(/[\0-\uffff]/);v=combinePrefixPatterns(b)})();var w=t.length;var x=/\S/;var y=function(a){var b=a.source,basePos=a.basePos;var c=[basePos,S];var d=0;var e=b.match(v)||[];var f={};for(var g=0,nTokens=e.length;g=5&&'lang-'===j.substring(0,5);if(l&&!(k&&typeof k[1]==='string')){l=false;j=V}if(!l){f[h]=j}}var n=d;d+=h.length;if(!l){c.push(basePos+n,j)}else{var o=k[1];var p=h.indexOf(o);var q=p+o.length;if(k[2]){q=h.length-k[2].length;p=q-o.length}var r=j.substring(5);appendDecorations(basePos+n,h.substring(0,p),y,c);appendDecorations(basePos+n+p,o,langHandlerForExtension(r,o),c);appendDecorations(basePos+n+q,h.substring(q),y,c)}}a.decorations=c};return y}function sourceDecorator(a){var b=[],fallthroughStylePatterns=[];if(a['tripleQuotedStrings']){b.push([M,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,'\'"'])}else if(a['multiLineStrings']){b.push([M,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,'\'"`'])}else{b.push([M,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,'"\''])}if(a['verbatimStrings']){fallthroughStylePatterns.push([M,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}if(a['hashComments']){if(a['cStyleComments']){b.push([O,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,'#']);fallthroughStylePatterns.push([M,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{b.push([O,/^#[^\r\n]*/,null,'#'])}}if(a['cStyleComments']){fallthroughStylePatterns.push([O,/^\/\/[^\r\n]*/,null]);fallthroughStylePatterns.push([O,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(a['regexLiterals']){var c=('/(?=[^/*])'+'(?:[^/\\x5B\\x5C]'+'|\\x5C[\\s\\S]'+'|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'+'/');fallthroughStylePatterns.push(['lang-regex',new RegExp('^'+Z+'('+c+')')])}var d=a['keywords'].replace(/^\s+|\s+$/g,'');if(d.length){fallthroughStylePatterns.push([N,new RegExp('^(?:'+d.replace(/\s+/g,'|')+')\\b'),null])}b.push([S,/^\s+/,null,' \r\n\t\xA0']);fallthroughStylePatterns.push([Q,/^@[a-z_$][a-z_$@0-9]*/i,null],[P,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,null],[S,/^[a-z_$][a-z_$@0-9]*/i,null],[Q,new RegExp('^(?:'+'0x[a-f0-9]+'+'|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'+'(?:e[+\\-]?\\d+)?'+')'+'[a-z]*','i'),null,'0123456789'],[R,/^.[^\s\w\.$@\'\"\`\/\#]*/,null]);return createSimpleLexer(b,fallthroughStylePatterns)}var br=sourceDecorator({'keywords':L,'hashComments':true,'cStyleComments':true,'multiLineStrings':true,'regexLiterals':true});function recombineTagsAndDecorations(c){var d=c.source;var e=c.extractedTags;var f=c.decorations;var g=[];var h=0;var j=null;var k=null;var l=0;var m=0;var n=makeTabExpander(window['PR_TAB_WIDTH']);var o=/([\r\n ]) /g;var p=/(^| ) /gm;var q=/\r\n?|\n/g;var r=/[ \r\n]$/;var s=true;var t=window['_pr_isIE6']();var u=(t?(c.sourceNode.tagName==='PRE'?(t===6?' \r\n':t===7?' 
    \r':' \r'):' 
    '):'
    ');var v=c.sourceNode.className.match(/\blinenums\b(?::(\d+))?/);var w;if(v){var x=[];for(var i=0;i<10;++i){x[i]=u+'
  • '}var y=v[1]&&v[1].length?v[1]-1:0;g.push('
    1. ');w=function(){var a=x[++y%10];return j?(''+a+''):a}}else{w=u}function emitTextUpTo(a){if(a>h){if(j&&j!==k){g.push('');j=null}if(!j&&k){j=k;g.push('')}var b=textToHtml(n(d.substring(h,a))).replace(s?p:o,'$1 ');s=r.test(b);g.push(b.replace(q,w));h=a}}while(true){var z;if(l');j=null}g.push(e[l+1]);l+=2}else if(m')}if(v){g.push('
    ')}c.prettyPrintedHtml=g.join('')}var bs={};function registerLangHandler(a,b){for(var i=b.length;--i>=0;){var c=b[i];if(!bs.hasOwnProperty(c)){bs[c]=a}else if('console'in window){console['warn']('cannot override language handler %s',c)}}}function langHandlerForExtension(a,b){if(!(a&&bs.hasOwnProperty(a))){a=/^\s*]*(?:>|$)/],[O,/^<\!--[\s\S]*?(?:-\->|$)/],['lang-',/^<\?([\s\S]+?)(?:\?>|$)/],['lang-',/^<%([\s\S]+?)(?:%>|$)/],[R,/^(?:<[%?]|[%?]>)/],['lang-',/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],['lang-js',/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],['lang-css',/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],['lang-in.tag',/^(<\/?[a-z][^<>]*>)/i]]),['default-markup','htm','html','mxml','xhtml','xml','xsl']);registerLangHandler(createSimpleLexer([[S,/^[\s]+/,null,' \t\r\n'],[X,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,'\"\'']],[[T,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[W,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],['lang-uq.val',/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[R,/^[=<>\/]+/],['lang-js',/^on\w+\s*=\s*\"([^\"]+)\"/i],['lang-js',/^on\w+\s*=\s*\'([^\']+)\'/i],['lang-js',/^on\w+\s*=\s*([^\"\'>\s]+)/i],['lang-css',/^style\s*=\s*\"([^\"]+)\"/i],['lang-css',/^style\s*=\s*\'([^\']+)\'/i],['lang-css',/^style\s*=\s*([^\"\'>\s]+)/i]]),['in.tag']);registerLangHandler(createSimpleLexer([],[[X,/^[\s\S]+/]]),['uq.val']);registerLangHandler(sourceDecorator({'keywords':D,'hashComments':true,'cStyleComments':true}),['c','cc','cpp','cxx','cyc','m']);registerLangHandler(sourceDecorator({'keywords':'null true false'}),['json']);registerLangHandler(sourceDecorator({'keywords':F,'hashComments':true,'cStyleComments':true,'verbatimStrings':true}),['cs']);registerLangHandler(sourceDecorator({'keywords':E,'cStyleComments':true}),['java']);registerLangHandler(sourceDecorator({'keywords':K,'hashComments':true,'multiLineStrings':true}),['bsh','csh','sh']);registerLangHandler(sourceDecorator({'keywords':I,'hashComments':true,'multiLineStrings':true,'tripleQuotedStrings':true}),['cv','py']);registerLangHandler(sourceDecorator({'keywords':H,'hashComments':true,'multiLineStrings':true,'regexLiterals':true}),['perl','pl','pm']);registerLangHandler(sourceDecorator({'keywords':J,'hashComments':true,'multiLineStrings':true,'regexLiterals':true}),['rb']);registerLangHandler(sourceDecorator({'keywords':G,'cStyleComments':true,'regexLiterals':true}),['js']);registerLangHandler(createSimpleLexer([],[[M,/^[\s\S]+/]]),['regex']);function applyDecorator(a){var b=a.sourceCodeHtml;var c=a.langExtension;a.prettyPrintedHtml=b;try{var d=extractTags(b);var f=d.source;a.source=f;a.basePos=0;a.extractedTags=d.tags;langHandlerForExtension(c,f)(a);recombineTagsAndDecorations(a)}catch(e){if('console'in window){console['log'](e&&e['stack']?e['stack']:e)}}}function prettyPrintOne(a,b){var c={sourceCodeHtml:a,langExtension:b};applyDecorator(c);return c.prettyPrintedHtml}function prettyPrint(f){function byTagName(a){return document.getElementsByTagName(a)}var g=[byTagName('pre')];var h=[];for(var i=0;iJavaScript Garden

    소개

    Intro

    JavaScript 언어의 핵심에 대한 내용을 모아 JavaScript Garden을 만들어 었다. 이 글이 초보자가 JavaScript 익히면서 자주 겪는 실수, 미묘한 버그, 성능 이슈, 나쁜 습관들 줄일 수 있도록 도와줄 것이다.

    +

    JavaScript Garden은 단순히 JavaScript 언어 자체를 설명하려 만들지 않았다. 그래서 이 글에서 설명하는 주제들을 이해하려면 반드시 언어에 대한 기본 지식이 필요하다. 먼저 Mozilla Developer Network에 있는 문서로 JavaScript 언어를 공부하기 바란다.

    +

    저자들

    +

    이 글은 Stack Overflow에서 사랑받는 두 사람 Ivo WetzelZhang Yi Jiang의 작품이다. Ivo Wetzel이 글을 썼고 Zhang Yi jiang이 디자인을 맡았다.

    +

    기여자들

    + +

    번역

    + +

    호스팅

    +

    JavaScript Garden은 Github에서 호스팅하고 있고 Cramer DevelopmentJavaScriptGarden.info에서 미러링해주고 있다.

    +

    저작권

    +

    JavaScript Garden은 MIT license를 따르고 GitHub에서 호스팅하고 있다. 문제를 발견하면 이슈를 보고하거나 수정해서 Pull Request를 하라. 아니면 Stack Overflow 채팅 사이트의 Javascript room에서 우리를 찾으라.

    +

    객체

    객체와 프로퍼티

    JavaScript에서 nullundefined를 제외한 모든 것들은 객체처럼 동작한다.

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

    숫자 리터럴은 객체처럼 사용되지 못할꺼라는 오해가 있는데 이것은 단지 JavaScript 파서의 문제일 뿐이다. JavaScript 파서는 숫자에 Dot Notation이 들어가면 오류라고 생각한다.

    +
    2.toString(); // SyntaxError가 난다.
    +

    하지만, 숫자를 객체처럼 사용할수 있는 꼼수가 몇 가지 있다.

    +
    2..toString(); // 두 번째 점은 잘 된다.
    +2 .toString(); // 왼쪽 공백이 있으면 잘 된다.
    +(2).toString(); // 2를 먼저 해석한다.
    +

    Object 타입

    +

    JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때문에 Hashmap처럼 사용될 수도 있다.

    +

    객체 리터럴인 Object Notation으로 객체를 만들면 Object.prototype을 상속받고 프로퍼티를 하나도 가지지 않은 객체가 만들어진다.

    +
    var foo = {}; // 깨끗한 새 객체를 만든다.
    +
    +// 값이 12인 'test' 프로퍼티가 있는 객체를 만든다.
    +var bar = {test: 12}; 
    +

    프로퍼티 접근

    +

    객체의 프로퍼티는 객체이름 다음에 점을 찍어(Dot Notation) 접근하거나 각괄호를 이용해(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
    +

    두 방식 모두 거의 동일하게 동작한다. 다만 차이가 있다면 각괄호 방식은 프로퍼티 이름을 동적으로 할당해서 값에 접근 할수 있지만 점을 이용한 방식은 구문 오류를 발생시킨다.

    +

    프로퍼티 삭제

    +

    객체의 프로퍼티를 삭제하려면 delete를 사용해야만 한다. 프로퍼티에 undefinednull을 할당하는 것은 프로퍼티를 삭제하는 것이 아니라 프로퍼티에 할당된 value만 지우고 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]);
    +    }
    +}
    +

    위 코드의 출력 결과는 baz만 제거했기 때문에 bar undefinedfoo null은 출력되고 baz와 관련된 것은 출력되지 않는다.

    +

    Notation of Keys

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

    프로퍼티는 따옴표 없는 문자열(plain characters)과 따옴표로 감싼 문자열(strings)을 모두 Key 값으로 사용할 수 있다. 하지만 위와 같은 코드는 JavaScript 파서의 잘못된 설계 때문에 구버전(ECMAScript 5 이전 버전)에서는 SystaxError가 발생할 것이다.

    +

    위 코드에서 문제가 되는 delete 키워드를 따옴표로 감싸면 구버전의 JavaScript 엔진에서도 제대로 해석될 것이다.

    +

    Prototype

    Javascript는 클래스 스타일의 상속 모델을 사용하지 않고 프로토타입 스타일의 상속 모델을 사용한다.

    +

    '이 점이 JavaScript의 약점이다.'라고 말하는 사람들도 있지만 실제로는 prototypal inheritance 모델이 훨씬 더 강력하다. 그 이유는 프로토타입 모델에서 클래스 모델을 흉내 내기는 매우 쉽지만, 반대로 클래스 모델에서 프로토타입 모델을 흉내 내기란 매우 어렵기 때문이다.

    +

    실제로 Prototypal Inheritance 모델을 채용한 언어 중에서 JavaScript만큼 널리 사용된 언어가 없었기 때문에 두 모델의 차이점이 다소 늦게 정리된 감이 있다.

    +

    먼저 가장 큰 차이점은 프로토타입 체인이라는 것을 이용해 상속을 구현한다는 점이다.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Foo의 인스턴스를 만들어 Bar의 prototype에 할당한다.
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Bar 함수를 생성자로 만들고
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // bar 인스턴스를 만든다.
    +
    +// 결과적으로 만들어진 프로토타입 체인은 다음과 같다. 
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo] 
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    위 코드에서 test 객체는 Bar.prototypeFoo.prototype을 둘 다 상속받았기 때문에 Foo에 정의한 method 함수에 접근할 수 있다. 그리고 프로토타입 체인에 있는 Foo 인스턴스의 value 프로퍼티도 사용할 수 있다. new Bar()를 해도 Foo 인스턴스는 새로 만들어지지 않고 Bar의 prototype에 있는 것을 재사용한다. 그래서 모든 Bar 인스턴스는 같은 value 프로퍼티를 공유한다.

    + +

    프로토타입 탐색

    +

    객체의 프로퍼티에 접근하려고 하면 JavaScript는 해당 이름의 프로퍼티를 찾을 때까지 프로토타입 체인을 거슬러 올라가면서 탐색하게 된다.

    +

    프로토타입 체인을 끝까지 탐색했음에도(보통은 Object.prototype임) 불구하고 원하는 프로퍼티를 찾지 못하면 undefined를 반환한다.

    +

    prototype 프로퍼티

    +

    prototype 프로퍼티는 프로토타입 체인을 만드는 데 사용하고 어떤 값이든 할당할 수 있지만, primitive 값을 할당되면 무시한다.

    +
    function Foo() {}
    +Foo.prototype = 1; // 무시됨
    +

    반면에 위 예제처럼 객체를 할당하면 프로토타입 체인이 동적으로 잘 만들어진다.

    +

    성능

    +

    프로토타입 체인을 탐색하는 시간이 오래걸릴수록 성능에 부정적인 영향을 줄수있다. 특히 성능이 중요한 코드에서 프로퍼티 탐색시간은 치명적인 문제가 될수있다. 가령, 없는 프로퍼티에 접근하려고 하면 항상 프로토타입 체인 전체를 탐색하게 된다.

    +

    뿐만아니라 객체를 순회(Iterate)할때도 프로토타입 체인에 있는 모든 프로퍼티를 탐색하게 된다.

    +

    네이티브 프로토타입의 확장

    +

    종종 Object.prototype을 이용해 내장 객체를 확장하는 경우가 있는데, 이것도 역시 잘못 설계된 것중에 하나다.

    +

    위와 같이 확장하는 것을 Monkey Patching라고 부르는데 캡슐화를 망친다. 물론 Prototype같은 유명한 프레임워크들도 이런 확장을 사용하지만, 기본 타입에 표준도 아닌 기능들을 너저분하게 추가하는 이유를 여전히 설명하지 못하고 있다.

    +

    기본 타입을 확장해야하는 유일한 이유는 Array.forEach같이 새로운 JavaScript 엔진에 추가된 기능을 대비해 미리 만들어 놓는 경우 말고는 없다.

    +

    결론

    +

    프로토타입을 이용해 복잡한 코드를 작성하기 전에 반드시 프로토타입 상속 (Prototypal Inheritance) 모델을 완벽하게 이해하고 있어야 한다. 뿐만아니라 프로토타입 체인과 관련된 성능 문제로 고생하지 않으려면 프로토타입 체인이 너무 길지 않도록 항상 주의하고 적당히 끊어줘야 한다. 마지막으로 새로운 JavaScript 기능에 대한 호환성 유지 목적이 아니라면 절대로 네이티브 프로토타입을 확장하지마라.

    +

    hasOwnProperty

    어떤 객체의 프로퍼티가 자기 자신의 프로퍼티인지 아니면 프로토타입 체인에 있는 것인지 확인하려면 hasOwnProperty 메소드를 사용한다. 그리고 이 메소드는 Object.prototype으로 부터 상속받아 모든 객체가 가지고 있다.

    + +

    hasOwnProperty메소드는 프로토타입 체인을 탐색하지 않고, 프로퍼티를 다룰수있는 유일한 방법이다.

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

    hasOwnProperty 메소드는 어떤 프로퍼티가 자기 자신의 프로퍼티인지 아닌지 정확하게 알려주기 때문에 객체의 프로퍼티를 순회할때 꼭 필요하다. 그리고 프로토타입 체인 어딘가에 정의된 프로퍼티만을 제외하는 방법은 없다.

    +

    hasOwnProperty 메소드도 프로퍼티다

    +

    JavaScript는 hasOwnProperty라는 이름으로 프로퍼티를 덮어 쓸수도 있다. 그래서 객체 안에 같은 이름으로 정의된 hasOwnProperty가 있을 경우, 본래 hasOwnProperty의 값을 정확하게 얻고 싶다면 다른 객체의 hasOwnProperty 메소드를 빌려써야 한다.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // 항상 false를 반환한다.
    +
    +// 다른 객체의 hasOwnProperty를 사용하여 foo 객체의 프로퍼티 유무를 확인한다.
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    +// Object에 있는 hasOwnProperty를 사용해도 된다.
    +Object.prototype.hasOwnProperty.call(obj, 'bar'); // true
    +

    결론

    +

    어떤 객체에 원하는 프로퍼티가 있는지 확인하는 가장 확실한 방법은 hasOwnProperty를 사용하는 것이다. for in loop에서 네이티브 객체에서 확장된 프로퍼티를 제외하고 순회하려면 hasOwnProperty와 함께 사용하길 권한다.

    +

    for in Loop

    객체의 프로퍼티를 탐색할때 in 연산자와 마찬가지로 for in 문도 프로토타입 체인까지 탐색한다.

    + +
    // Object.prototype을 오염시킨다.
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // bar와 moo 둘 다 출력한다.
    +}
    +

    for in문에 정의된 기본 동작을 바꿀순 없기 때문에 루프 안에서 불필요한 프로퍼티를 필터링 해야한다. 그래서 Object.prototypehasOwnProperty메소드를 이용해 본래 객체의 프로퍼티만 골라낸다.

    + +

    hasOwnProperty로 필터링 하기

    +
    // 위의 예제에 이어서 
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    위와 같이 사용해야 올바른 사용법이다. hasOwnProperty 때문에 오직 moo만 출력된다. hasOwnProperty가 없으면 이 코드는 Object.prototype으로 네이티브 객체가 확장될 때 에러가 발생할 수 있다.

    +

    따라서 Proptotype 라이브러리처럼 네이티브 객체를 프로토타입으로 확장한 프레임워크를 사용할 경우 for in 문에 hasOwnProperty를 사용하지 않을 경우 문제가 발생할 수 있다.

    +

    결론

    +

    hasOwnProperty를 항상 사용하길 권한다. 실제 코드가 동작하는 환경에서는 절대로 네이티브 객체가 프로토타입으로 확장됐다 혹은 확장되지 않았다를 가정하면 안된다.

    +

    함수

    함수 선언과 함수 표현식

    JavaScript에서 함수는 First Class Object다. 즉, 함수 자체가 또 다른 함수의 인자될 수 있다는 말이다. 그래서 익명 함수를 비동기 함수의 콜백으로 넘기는 것도 이런 특징을 이용한 일반적인 사용법이다.

    +

    함수 선언

    +
    function foo() {}
    +

    위와 같이 선언한 함수는 프로그램이 실행하기 전에 먼저 호이스트(Hoist) (스코프가 생성)되기 때문에 정의된 스코프(Scope) 안에서는 어디서든 이 함수를 사용할 수 있다. 심지어 함수를 정의하기 전에 호출해도 된다.

    +
    foo(); // 이 코드가 실행되기 전에 foo가 만들어지므로 잘 동작한다.
    +function foo() {}
    +

    함수 표현식

    +
    var foo = function() {};
    +

    위 예제는 foo 변수에 익명 함수를 할당한다.

    +
    foo; // 'undefined'
    +foo(); // TypeError가 난다.
    +var foo = function() {};
    +

    'var'문을 이용해 선언하는 경우, 코드가 실행되기 전에 'foo' 라는 이름의 변수를 스코프의 맨 위로 올리게 된다.(호이스트 된다) 이때 foo 값은 undefiend로 정의된다.

    +

    하지만 변수에 값을 할당하는 일은 런타임 상황에서 이루어지게 되므로 실제 코드가 실행되는 순간의 foo변수는 기본 값인 undefined이 된다.

    +

    이름있는 함수 표현식

    +

    이름있는 함수를 할당할때도 특이한 경우가 있다.

    +
    var foo = function bar() {
    +    bar(); // 이 경우는 동작 하지만, 
    +}
    +bar(); // 이 경우는 참조에러를 발생시킨다. 
    +

    foo 함수 스코프 밖에서는 foo 변수 외에는 다른 값이 없기 때문에 bar는 함수 밖에서 사용할 수 없지만 함수 안에서는 사용할 수 있다. 이와 같은 방법으로 자바스크립트에서 어떤 함수의 이름은 항상 그 함수의 지역 스코프 안에서 사용할수있다.

    +

    this의 동작 원리

    다른 프로그래밍 언어에서 this가 가리키는 것과 JavaScript에서 this가 가리키는 것과는 좀 다르다. this가 가리킬 수 있는 객체는 정확히 5종류나 된다.

    +

    Global Scope에서

    +
    this;
    +

    Global Scope에서도 this가 사용될 수 있고 이때에는 Global 객체를 가리킨다.

    +

    함수를 호출할 때

    +
    foo();
    +

    이때에도 thisGlobal 객체를 가리킨다.

    + +

    메소드로 호출할 때

    +
    test.foo(); 
    +

    이 경우에는 thistest를 가리킨다.

    +

    생성자를 호출할 때

    +
    new foo(); 
    +

    new 키워드로 생성자를 실행시키는 경우에 이 생성자 안에서 this는 새로 만들어진 객체를 가리킨다.

    +

    this가 가리키는 객체 정해주기.

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // a = 1, b = 2, c = 3으로 넘어간다.
    +foo.call(bar, 1, 2, 3); // 이것도... 
    +

    Function.prototypecall이나 apply 메소드를 호출하면 this가 무엇을 가리킬지 정해줄 수 있다. 호출할 때 첫 번째 인자로 this가 가리켜야 할 객체를 넘겨준다.

    +

    그래서 foo Function 안에서 this는 위에서 설명했던 객체 중 하나를 가리키는 것이 아니라 bar를 가리킨다.

    + +

    대표적인 함정

    +

    this가 Global 객체를 가리키는 것도 잘못 설계된 부분 중 하나다. 괜찮아 보이지만 실제로는 전혀 사용하지 않는다.

    +
    Foo.method = function() {
    +    function test() {
    +        // 여기에서 this는 Global 객체를 가리킨다.
    +    }
    +    test();
    +};
    +

    test 에서 thisFoo를 가리킬 것으로 생각할 테지만 틀렸다. 실제로는 그렇지 않다.

    +

    test에서 Foo에 접근하려면 method에 Local 변수를 하나 만들고 Foo를 가리키게 하여야 한다.

    +
    Foo.method = function() {
    +    var self = this;
    +    function test() {
    +        // 여기에서 this 대신에 self를 사용하여 Foo에 접근한다
    +    }
    +    test();
    +};
    +

    self는 통상적인 변수 이름이지만, 바깥쪽의 this를 참조하기 위해 일반적으로 사용된다. +또한 클로저와 결합하여 this의 값을 주고 받는 용도로 사용할 수도 있다.

    +

    ECMAScript 5부터는 익명 함수와 결합된 bind 메소드를 사용하여 같은 결과를 얻을 수 있다.

    +
    Foo.method = function() {
    +    var test = function() {
    +        // this는 이제 Foo를 참조한다
    +    }.bind(this);
    +    test();
    +};
    +

    Method 할당하기

    +

    JavaScript의 또다른 함정은 바로 함수의 별칭을 만들수 없다는 점이다. 별칭을 만들기 위해 메소드를 변수에 넣으면 자바스크립트는 별칭을 만들지 않고 바로 할당해 버린다.

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

    첫번째 코드로 인해 이제 test는 다른 함수와 똑같이 동작한다. 그래서 test 함수 내부의 this도 더이상 someObject를 가리키지 않는다. (역주: test가 methodTest의 별칭이라면 methodTest 함수 내부의 this도 someObject를 똑같이 가리켜야 하지만 test의 this는 더이상 someObject가 아니다.)

    +

    이렇게 this를 늦게 바인딩해서 나타나는 약점때문에 늦은 바인딩이 나쁜 거라고 생각할수도 있지만, 사실 이런 특징으로 인해 프로토타입 상속(prototypal inheritance)도 가능해진다.

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

    Bar 인스턴스에서 method를 호출하면 method에서 this는 바로 그 인스턴스를 가리킨다.

    +

    클로져(Closure)와 참조(Reference)

    클로져는 JavaScript의 특장점 중 하나다. 클로저를 만들면 클로저 스코프 안에서 클로저를 만든 외부 스코프(Scope)에 항상 접근할 있다. JavaScript에서 스코프는 함수 스코프밖에 없기 때문에 기본적으로 모든 함수는 클로저가 될수있다.

    +

    private 변수 만들기

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

    여기서 Counterincrement 클로저와 get 클로저 두 개를 반환한다. 이 두 클로저는 Counter 함수 스코프에 대한 참조를 유지하고 있기 때문에 이 함수 스코프에 있는 count 변수에 계속 접근할 수 있다.

    +

    Private 변수의 동작 원리

    +

    JavaScript에서는 스코프(Scope)를 어딘가에 할당해두거나 참조할수 없기 때문에 스코프 밖에서는 count 변수에 직접 접근할 수 없다. 접근할수 있는 유일한 방법은 스코프 안에 정의한 두 클로저를 이용하는 방법밖에 없다.

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

    위 코드에서 foo.hack 함수는 Counter 함수 안에서 정의되지 않았기 때문에 이 함수가 실행되더라도 Counter 함수 스코프 안에 있는 count 값은 변하지 않는다. 대신 foo.hack 함수의 countGlobal 스코프에 생성되거나 이미 만들어진 변수를 덮어쓴다.

    +

    반복문에서 클로저 사용하기

    +

    사람들이 반복문에서 클로저를 사용할 때 자주 실수를 하는 부분이 있는데 바로 인덱스 변수를 복사할때 발생한다.

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

    이 코드는 0부터 9까지의 수를 출력하지 않고 10만 열 번 출력한다.

    +

    타이머에 설정된 익명 함수는 변수 i에 대한 참조를 들고 있다가 console.log가 호출되는 시점에 i의 값을 사용한다. console.log가 호출되는 시점에서 for loop는 이미 끝난 상태기 때문에 i 값은 10이 된다.

    +

    기대한 결과를 얻으려면 i 값을 복사해 두어야 한다.

    +

    앞의 참조 문제 해결하기

    +

    반복문의 index 값을 복사하는 가장 좋은 방법은 익명함수로 랩핑Anonymous Wrapper하는 방법이다.

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

    이 익명 함수에 i를 인자로 넘기면 이 함수의 파라미터 e에 i의 이 복사되어 넘어갈 것이다.

    +

    그리고 setTimeout는 익명 함수의 파라미터인 e에 대한 참조를 갖게 되고 e값은 복사되어 넘어왔으므로 loop의 상태에 따라 변하지 않는다.

    +

    또다른 방법으로 랩핑한 익명 함수에서 출력 함수를 반환하는 방법도 있다. 아래 코드는 위 코드와 동일하게 동작한다.

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

    즐겨 쓰이는 또 하나의 방법은 setTimeout 함수에 세번째 인자를 추가하는 방법이다. +추가된 인자는 콜백 함수에 전달된다.

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

    레거시 JS 환경(Internet Explorer 9 이하)은 이 방법을 지원하지 않는다.

    +

    .bind를 사용하는 방법도 있다. .bindthis 컨텍스트와 인자들을 함수에 결속(bind)시킨다. +아래 코드는 위 코드와 동일하게 동작한다.

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

    arguments 객체

    JavaScript의 모든 함수 스코프에는 arguments라는 특별한 변수가 있다. 이 변수는 함수에 넘겨진 모든 인자에 대한 정보가 담겨 있다.

    + +

    arguments 객체는 Array가 아니다. 물론 length 프로퍼티도 있고 여러모로 Array와 비슷하게 생겼지만 Array.prototype을 상속받지는 않았다.

    +

    그래서 arguments에는 push, pop, slice 같은 표준 메소드가 없다. 일반 for문을 이용해 순회는 할수 있지만, Array의 메소드를 이용하려면 arguments를 Array로 변환해야 한다.

    +

    Array로 변환하기

    +

    다음 코드는 arguments에 있는 객체를 새로운 Array에 담아 반환한다.

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

    이 변환 과정은 느리기 때문에 성능이 중요한 부분에 사용하는 것은 별로 바람직하지 못 하다.

    +

    arguemnts 객체 넘기기

    +

    어떤 함수에서 다른 함수로 arguments 객체를 넘길 때에는 다음과 같은 방법을 권한다. (역주: foo 함수는 bar 함수 한번 랩핑한 함수다. )

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // 내곡동에 땅이라도 산다.
    +}
    +

    또 다른 트릭은 callapply를 함께 사용하여 메소드(this의 값과 인자들을 사용하는 함수)를 +단지 인자들만 사용하는 일반 함수로 바꾸는 것이다.

    +
    function Person(first, last) {
    +  this.first = first;
    +  this.last = last;
    +}
    +
    +Person.prototype.fullname = function(joiner, options) {
    +  options = options || { order: "western" };
    +  var first = options.order === "western" ? this.first : this.last;
    +  var last =  options.order === "western" ? this.last  : this.first;
    +  return first + (joiner || " ") + last;
    +};
    +
    +// "fullname" 메소드의 비결합(unbound) 버전을 생성한다.
    +// 첫번째 인자로 'first'와 'last' 속성을 가지고 있는 어떤 객체도 사용 가능하다.
    +// "fullname"의 인자 개수나 순서가 변경되더라도 이 랩퍼를 변경할 필요는 없을 것이다.
    +Person.fullname = function() {
    +  // 결과: Person.prototype.fullname.call(this, joiner, ..., argN);
    +  return Function.call.apply(Person.prototype.fullname, arguments);
    +};
    +
    +var grace = new Person("Grace", "Hopper");
    +
    +// 'Grace Hopper'
    +grace.fullname();
    +
    +// 'Turing, Alan'
    +Person.fullname({ first: "Alan", last: "Turing" }, ", ", { order: "eastern" });
    +

    일반 파라미터와 arguments 객체의 인덱스

    +

    일반 파라미터와 arguments 객체의 프로퍼티는 모두 gettersetter를 가진다.

    +

    그래서 파라미터나 arguments 객체의 프로퍼티의 값을 바꾸면 둘 다 바뀐다.

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

    성능에 대한 오해와 진실.

    +

    arguments 객체는 항상 만들어지지만 두가지 예외사항이 있다. arguments라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 arguemnts 객체는 만들어지지 않는다. 하지만 뭐 이런 경우들은 어차피 arguments 객체를 안쓰겠다는 의미니까 상관 없다.

    +

    그리고 gettersetter는 항상 생성되기 때문에 getter/setter를 사용하는 것은 성능에 별 영향을 끼치지 않는다. 예제처럼 단순한 코드가 아니라 arguments 객체를 다방면으로 활용하는 실제 코드에서도 마찬가지다.

    + +

    그러나 예외도 있다. 최신 JavaScript 엔진에서 arguments.callee를 사용하면 성능이 확 떨어진다.

    +
    function foo() {
    +    arguments.callee; // 이 함수를 가리킨다.
    +    arguments.callee.caller; // 이 함수를 호출한 부모함수를 가리킨다.
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // 원래 인라인 돼야 하는디...
    +    }
    +}
    +

    위 코드에서 'foo' 함수는 자기 자신과 자신을 호출한 함수를 알아야 하기 때문에 더이상 인라인되지 않는다. 이렇게 쓰면 인라인이 주는 성능상 장점을 포기해야 하는데다가 이 함수가 호출되는 상황(calling context)에 의존하게 돼 버려서 캡슐화(Encapsulation)도 해친다. +(역주: 보통 코드가 컴파일 될때 코드를 인라인 시키면서 최적화 하는데, 위와 같이 arguments.callee나 caller를 사용하게 되면 런타임시에 해당 함수가 결정되므로 인라인 최적화를 할수가 없다.)

    +

    arguments.callee와 arguments.callee의 프로퍼티들은 절대 사용하지 말자!.

    + +

    생성자

    JavaScript의 생성자는 다른 언어들과 다르게 new 키워드로 호출되는 함수가 생성자가 된다.

    +

    생성자로 호출된 함수의 this 객체는 새로 생성된 객체를 가리키고, 새로 만든 객체의 prototype에는 생성자의 prototype이 할당된다.

    +

    그리고 생성자에 명시적인 return 구문이 없으면 this가 가리키는 객체를 반환한다.

    +
    function Person(name) {
    +    this.name = name;
    +}
    +
    +Person.prototype.logName = function() {
    +    console.log(this.name);
    +};
    +
    +var sean = new Person();
    +

    위 코드는 Person을 생성자로 호출하고 새로 생성된 객체의 prototypePerson.prototype으로 설정한다.

    +

    아래 코드와 같이 생성자에 명시적인 return 문이 있는 경우에는 반환하는 값이 객체인 경우에만 그 값을 반환한다.

    +
    function Car() {
    +    return 'ford';
    +}
    +new Car(); // 'ford'가 아닌 새로운 객체를 반환
    +
    +function Person() {
    +    this.someValue = 2;
    +
    +    return {
    +        name: 'Charles'
    +    };
    +}
    +new Person(); // someValue가 포함되지 않은 ({name:'Charles'}) 객체 반환
    +

    new 키워드가 없으면 그 함수는 객체를 반환하지 않는다.

    +
    function Pirate() {
    +    this.hasEyePatch = true; // 전역 객체를 준비!
    +}
    +var somePirate = Pirate(); // somePirate = undefined
    +

    위 예제는 그때그때 다르게 동작한다. 그리고 this 객체의 동작 원리에 따라서 Foo 함수안의 this의 값은 Global 객체를 가리키게된다. +(역주: 결국 new 키워드를 빼고, 코드를 작성할 경우 원치 않은 this 참조 오류가 발생할 수 있다.)

    +

    팩토리

    +

    생성자가 객체를 반환하면 new 키워드를 생략할 수 있다.

    +
    function Robot() {
    +    var color = 'gray';
    +    return {
    +        getColor: function() {
    +            return color;
    +        }
    +    }
    +}
    +Robot.prototype = {
    +    someFunction: function() {}
    +};
    +
    +new Robot();
    +Robot();
    +

    new 키워드의 유무과 관계없이 Robot 생성자의 동작은 동일하다. 즉 클로저가 할당된 method 프로퍼티가 있는 새로운 객체를 만들어 반환한다.

    +

    new Robot()으로 호출되는 생성자는 반환되는 객체의 prototype 프로퍼티에 아무런 영향을 주지 않는다. 객체를 반환하지 않는 생성자로 만들어지는 경우에만 객체의 prototype이 생성자의 것으로 할당된다.

    +

    그러니까 이 예제에서 new 키워드의 유무는 아무런 차이가 없다. +(역주: 생성자에 객체를 만들어 명시적으로 반환하면 new 키워드에 관계없이 잘 동작하는 생성자를 만들수있다. 즉, new 키워드가 빠졌을때 발생하는 this 참조 오류를 방어해준다.)

    +

    팩토리로 객체 만들기

    +

    new 키워드를 빼먹었을 때 버그가 생긴다는 이유로 아예 new를 사용하지 말 것을 권하기도 한다.

    +

    객체를 만들고 반환해주는 팩토리를 사용하여 new 키워드 문제를 회피할 수 있다.

    +
    function CarFactory() {
    +    var car = {};
    +    car.owner = 'nobody';
    +
    +    var milesPerGallon = 2;
    +
    +    car.setOwner = function(newOwner) {
    +        this.owner = newOwner;
    +    }
    +
    +    car.getMPG = function() {
    +        return milesPerGallon;
    +    }
    +
    +    return car;
    +}
    +

    new 키워드가 없어도 잘 동작하고 private 변수를 사용하기도 쉽다. 그렇지만, 단점도 있다.

    +
      +
    1. prototype으로 메소드를 공유하지 않으므로 메모리를 좀 더 사용한다.
    2. +
    3. 팩토리를 상속하려면 모든 메소드를 복사하거나 객체의 prototype에 객체를 할당해 주어야 한다.
    4. +
    5. new 키워드를 누락시켜서 prototype chain을 끊어버리는 것은 아무래도 언어의 의도에 어긋난다.
    6. +
    +

    결론

    +

    new 키워드가 생략되면 버그가 생길 수 있지만 그렇다고 prototype을 사용하지 않을 이유가 되지 않는다. 애플리케이션에 맞는 방법을 선택하는 것이 나을 거고 어떤 방법이든 *엄격하고 한결같이 지켜야 한다.

    +

    스코프와 네임스페이스

    JavaScript는 '{}' Block이 배배 꼬여 있어도 문법적으로는 잘 처리하지만, Block Scope은 지원하지 않는다. 그래서 JavaScript에서는 항상 함수 스코프를 사용한다.

    +
    function test() { // Scope
    +    for(var i = 0; i < 10; i++) { // Scope이 아님
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    + +

    그리고 JavaScript에는 Namepspace 개념이 없기 때문에 모든 값이 하나의 전역 스코프에 정의된다.

    +

    변수를 참조 할 때마다 JavaScript는 해당 변수를 찾을 때까지 상위 방향으로 스코프를 탐색한다. 변수 탐색하다가 전역 스코프에서도 찾지 못하면 ReferenceError를 발생시킨다.

    +

    전역 변수 문제.

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

    이 두 스크립트는 전혀 다르다. Script A는 전역 스코프에 foo라는 변수를 정의하는 것이고 Script B는 스코프에 변수 foo를 정의하는 것이다.

    +

    다시 말하지만, 이 둘은 전혀 다르고 var가 없을 때 특별한 의미가 있다.

    +
    // Global Scope
    +var foo = 42;
    +function test() {
    +    // local Scope
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    test 함수 안에 있는 'foo' 변수에 var 구문을 빼버리면 Global Scope의 foo의 값을 바꿔버린다. '뭐 이게 뭐가 문제야'라고 생각될 수 있지만 수천 줄인 JavaScript 코드에서 var를 빼먹어서 생긴 버그를 해결하는 것은 정말 어렵다.

    +
    // 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++) { // var가 없다.
    +        // 내가 for문도 해봐서 아는데...
    +    }
    +}
    +

    subLoop 함수는 전역 변수 i의 값을 변경해버리기 때문에 외부에 있는 for문은 subLoop을 한번 호출하고 나면 종료된다. 두 번째 for문에 var를 사용하여 i를 정의하면 이 문제는 생기지 않는다. 즉, 의도적으로 외부 스코프의 변수를 사용하는 것이 아니라면 var를 꼭 넣어야 한다.

    +

    지역 변수

    +

    JavaScript에서 지역 변수는 함수의 파라미터var로 정의한 변수밖에 없다.

    +

    // 전역 공간 + var foo = 1; + var bar = 2; + var i = 2;

    +
    function test(i) {
    +    // test 함수의 지역 공간
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    foo 변수와 i 변수는 test함수 스코프에 있는 지역 변수라서 전역 공간에 있는 foo, i 값은 바뀌지 않는다. 하지만 bar는 전역 변수이기 때문에 전역 공간에 있는 bar의 값이 변경된다.

    +

    호이스팅(Hoisting)

    +

    JavaScript는 선언문을 모두 호이스트(Hoist)한다. 호이스트란 var 구문이나 function 선언문을 해당 스코프의 맨 위로 옮기는 것을 말한다.

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

    코드를 본격적으로 실행하기 전에 JavaScript는 var 구문과 function 선언문을 해당 스코프의 맨위로 옮긴다.

    +
    // var 구문이 여기로 옮겨짐.
    +var bar, someValue; // default to 'undefined'
    +
    +// function 선언문도 여기로 옮겨짐
    +function test(data) {
    +    var goo, i, e; // Block Scope은 없으므로 local 변수들은 여기로 옮겨짐
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // bar()가 아직 'undefined'이기 때문에 TypeError가 남
    +someValue = 42; // Hoisting은 할당문은 옮기지 않는다.
    +bar = function() {};
    +
    +test();
    +

    블록 스코프(Block Scope)는 없으므로 for문과 if문 안에 있는 var 구문들까지도 모두 함수 스코프 앞쪽으로 옮겨진다. 그래서 if Block의 결과는 좀 이상해진다.

    +

    원래 코드에서 if Block은 전역 변수 goo를 바꾸는 것처럼 보였지만 호이스팅(Hoisting) 후에는 지역 변수를 바꾼다.

    +

    호이스팅을 모르면 다음과 같은 코드는 ReferenceError를 낼 것으로 생각할 것이다.

    +
    // SomeImportantThing이 초기화됐는지 검사한다.
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    var 구문은 전역 스코프의 맨위로 옮겨지기 때문에 이 코드는 잘 동작한다.

    +
    var SomeImportantThing;
    +
    +// SomeImportantThing을 여기서 초기화하거나 말거나...
    +
    +// SomeImportantThing는 선언돼 있다.
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    이름 찾는 순서

    +

    JavaScript의 모든 Scope은 현 객체를 가리키는 this를 가지고 있다. 전역 스코프에도 this가 있다.

    +

    함수 스코프에는 arguments라는 변수가 하나 더 있다. 이 변수는 함수에 인자로 넘겨진 값들이 담겨 있다.

    +

    예를 들어 함수 스코프에서 foo라는 변수에 접근할 때 JavaScript는 다음과 같은 순서로 찾는다.

    +
      +
    1. 해당 Scope에서 var foo 구문으로 선언된 것을 찾는다.
    2. +
    3. Function 파라미터에서 foo라는 것을 찾는다.
    4. +
    5. 해당 Function 이름이 foo인지 찾는다.
    6. +
    7. 상위 Scope으로 있는지 확인하고 있으면 #1부터 다시 한다.
    8. +
    + +

    네임스페이스

    +

    JavaScript에서는 전역 공간(Namepspace) 하나밖에 없어서 변수 이름이 중복되기 쉽다. 하지만 이름없는 랩퍼(Anonymous Wrappers)를 통해 쉽게 피해갈 수 있다.

    +
    (function() {
    +    // 일종의 네임스페이스라고 할 수 있다.
    +
    +    window.foo = function() {
    +        // 이 클로저는 전역 스코프에 노출된다.
    +    };
    +
    +})(); // 함수를 정의하자마자 실행한다.
    +

    이름없는 함수는 표현식(expressions)이기 때문에 호출되려면 먼저 평가(Evaluate)돼야 한다.

    +
    ( // 소괄호 안에 있는 것을 먼저 평가한다.
    +function() {}
    +) // 그리고 함수 객체를 반환한다.
    +() // 평가된 결과를 호출한다.
    +

    함수를 평가하고 바로 호출하는 방법이 몇가지 더 있다. 문법은 다르지만 똑같다.

    +
    // 함수를 평가하자마자 호출하는 방법들...
    +!function(){}();
    ++function(){}();
    +(function(){}());
    +// 등등...
    +

    결론

    +

    코드를 캡슐화할 때는 항상 이름없는 랩퍼(Anonymous Wrapper)로 네임스페이스를 만들어 사용할 것을 추천한다. 이 래퍼(Wrapper)는 이름이 중복되는 것을 막아 주고 더 쉽게 모듈화할 수 있도록 해준다.

    +

    그리고 전역 변수를 사용하는 것은 좋지 못한 습관이다. 이유야 어쨌든 에러 나기 쉽고 관리하기도 어렵다.

    +

    Array

    배열 순회와 프로퍼티

    JavaScript에서는 배열(Array)도 객체(Object)지만 객체 순회(Iterate)를 할 때 for in을 사용해서 좋을 게 없다. 실제로 배열을 탐색할때 for in문 사용하지 말아야 할 이유가 매우 많다.

    + +

    for in은 프로토타입 체인에 있는 프로퍼티를 모두 훑는(enumerate) 데다가 객체 자신의 프로퍼티만 훑으려면 hasOwnProperty를 사용해야 하기 때문에 for보다 20배 느리다.

    +

    배열 순회

    +

    배열을 순회 할때는 일반적인 for문을 사용하는 것이 가장 빠르다.

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

    이 예제에서 l = list.length로 배열의 length 값을 캐시해야 한다는 것을 꼭 기억해야 한다.

    +

    매번 반복할때마다 배열에 있는 length 프로퍼티에 접근하는 것은 좀 부담스럽다. 최신 JavaScript 엔진은 이 일을 알아서 처리해주기도 하지만 코드가 늘 새 엔진에서 실행되도록 보장할 방법이 없다.

    +

    실제로 캐시 하지 않으면 성능이 반으로 줄어든다.

    +

    length 프로퍼티

    +

    length 프로퍼티의 getter는 단순히 Array 안에 있는 엘리먼트의 개수를 반환하고 setter는 배열을 할당한 수만큼 잘라 버린다.

    +
    var arr = [1, 2, 3, 4, 5, 6];
    +arr.length = 3;
    +arr; // [1, 2, 3]
    +
    +arr.length = 6;
    +arr.push(4);
    +arr; // [1, 2, 3, undefined, undefined, undefined, 4]
    +

    현재 크기보다 더 작은 값을 할당하면 배열을 자른다. 배열의 크기를 증가시키면 드문드문(sparse)한 배열을 생성한다.

    +

    결론

    +

    최적의 성능을 위해서는 for문을 사용하고 length 프로퍼티 값을 캐시해야 한다. 배열에 for in을 사용하면 성능도 떨어지고 버그 나기도 쉽다.

    +

    배열 생성자

    배열을 만들때 배열 생성자에 파라미터를 넣어 만드는 방법은 헷갈릴수있다. 그래서 항상 각 괄호([]) 노테이션을 이용해 배열을 만들 것을 권한다

    +
    [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']
    +

    배열 생성자에 숫자를 인자로 넣으면 그 숫자 크기 만큼의 빈 배열을 반환한다. 즉 배열의 length는 그 숫자가 된다. 이때 생성자는 단지 length 프로퍼티에 그 숫자를 할당하기만 하고 배열은 실제로 초기화 하지도 않는다.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, 이 인덱스는 초기화되지 않음.
    +

    for문을 사용하지 않고 문자열을 더하는 경우에는 length 프로퍼티에 숫자를 할당해주는 기능이 유용할 때도 있다.

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

    결론

    +

    배열 생성자는 가능하면 사용하지 말고, 각 괄호 ([]) 노테이션이을 사용하자. 후자가 더 간략하고 명확할 뿐만 아니라 보기도 좋다.

    +

    타입

    객체 비교하기

    JavaScript에서 객체를 비교하는 방법은 두 가지가 있다.

    +

    이중 등호 연산자

    +

    이중 등호 연산자는 ==을 말한다.

    +

    JavaScript는 Weak Typing을 따르기 때문에 이중 등호를 이용해 비교할 때 두 객체의 자료형을 강제로 변환한다.

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

    이 표는 이중 등호를 사용하면 왜 안되는지를 보여준다. 이 복잡한 변환 규칙은 실제로 골치 아픈 버그를 만들어 낸다.

    +

    게다가 강제로 타입을 변환하게 되면 성능에도 영향을 준다. 예를 들어 문자와 숫자를 비교하려면 반드시 먼저 문자를 숫자로 변환해야 한다.

    +

    삼중 등호 연산자

    +

    삼중 등호 연산자는 ===을 말한다.

    +

    삼중 등호는 강제로 타입을 변환하지 않는다는 사실을 제외하면 이중 등호와 동일하다.

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

    위 결과가 훨씬 더 명확하고 문제가 쉽게 드러난다. 삼중 등호를 사용하면 코드를 좀 더 튼튼하게 만들수 있고, 비교하는 두 객체의 타입이 다르면 더 좋은 성능을 얻을 수도 있다.

    +

    객체 비교하기

    +

    이중 등호와(==)와 삼중 등호(===)는 둘 다 값을 비교하는 연산이지만 피연산자중에 Object 타입이 하나라도 있으면 다르게 동작한다.

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

    두 연산자 모두 두 객체의 값이 같은지를 비교하지 않고, 두 객체가 같은 객체(identity)인지를 비교한다. C에서 포인터를 비교하거나 Python의 is처럼 같은 인스턴스인지 비교하는 것이다.

    +

    결론

    +

    삼중 등호 연산자를 사용할 것을 강력하게 권한다. 비교하기 위해서 타입 변환이 필요하면 언어의 복잡한 변환 규칙에 맡기지 말고 꼭 명시적으로 변환한 후에 비교해야 한다.

    +

    typeof 연산자

    typeof 연산자도 instanceof 연산자와 함께 JavaScript에서 치명적으로 잘못 설계된 부분이다. 이건 정말이지 아무짝에도 쓸모가 없다.

    +

    instanceof 연산자는 그래도 여전히 쓸만한 데가 좀 있는데 typeof 연산자는 객체의 타입을 검사하는 것 외에는 쓸만한데가 없고, 이마저도 거의 쓸일이 없다.

    + +

    JavaScript 타입 표

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

    위 표에서 Typetypeof가 반환하는 값이다. 위 표에서처럼 일치되는 값이 거의 없다.

    +

    위 표에서 Class는 객체 내부에 있는 [[Class]] 프로퍼티의 값을 말한다.

    + +

    [[Class]] 프로퍼티의 값을 가져다 쓰려면 Object.prototypetoString 메소드를 사용한다.

    +

    객체의 클래스

    +

    표준에 의하면 [[Class]] 값을 얻는 방법은 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
    +

    Object.prototype.toStringthis[[Class]] 값을 가져오는 것이니까 this를 obj로 바꾸어 사용한다.

    + +

    변수가 Undefined인지 확인하기

    +
    typeof foo !== 'undefined'
    +

    위 코드는 foo가 정의됐는지 아닌지를 확인해준다. 정의되지 않은 변수에 접근하면 ReferenceError 나는데 이것을 방지할 수 있다. typeof가 유용한 건 이때뿐이다.

    +

    결론

    +

    객체의 타입을 검사하려면 Object.prototype.toString를 사용해야 한다. 다른 방법은 신뢰할 수 없다. 위 표에서 보여준 것처럼 typeof가 반환하는 값은 표준에 나와 있지 않기 때문에 구현방법도 제각각이다.

    +

    변수가 정의됐는지 확인할 때를 제외하고 가급적 typeof는 피해야한다.

    +

    instanceof 연산자

    instanceof연산자는 두 피연산자의 생성자를 비교할때 사용하고 직접 만든 객체를 비교할 때 매우 유용하다. 내장 타입에 쓰는 경우에는 typeof처럼 거의 쓸모가 없다.

    +

    커스텀 객체를 intanceof로 비교하기

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Bar.prototype에 함수 객체인 Foo를 할당하면
    +// Bar의 인스턴스는 Foo의 인스턴스가 아니다.
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    기본 내장 객체 타입을 intanceof로 비교하기

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

    JavaScript 컨텍스트마다(웹 브라우저의 도큐먼트 같은) 객체의 생성자는 다를 수밖에 없어서 instanceof는 다른 JavaScript 컨텍스트에 있는(웹 브라우저의 다른 도큐먼트에 있는) 객체와는 비교할 수 없다.

    +

    결론

    +

    instanceof는 한 JavaScript 컨텍스트 내에서 사용자가 만든 타입의 객체를 비교할 때에만 유용하다. typeof처럼 다른 목적으로는 사용하지 않는 것이 좋다.

    +

    타입 캐스팅

    JavaScript는 Weak Typing 언어이기 때문에 필요할 때마다 알아서 타입을 변환한다.

    +
    // 다음은 모두 true
    +new Number(10) == 10; // Number.toString()이 호출되고 
    +                      // 다시 Number로 변환된다.
    +
    +10 == '10';           // 스트링은 Number로 변환된다.
    +10 == '+10 ';         // 이상한 스트링
    +10 == '010';          // 엉뚱한 스트링
    +isNaN(null) == false; // null은 NaN이 아녀서 0으로 변환된다.
    +
    +// 다음은 모두 false
    +10 == 010;
    +10 == '-10';
    + +

    위와 같은 문제들은 *반드시 삼중 등호 연산자를 이용해 해결하길 권한다. 물론 삼중 등호로 많은 결점을 보완할 수 있지만, 여전히 weak typing 시스템 때문에 생기는 많은 문제가 남아있다.

    +

    기본 타입 생성자

    +

    NumberString 같은 기본 타입들의 생성자는 new 키워드가 있을 때와 없을 때 다르게 동작한다.

    +
    new Number(10) === 10;     // False, Object와 Number
    +Number(10) === 10;         // True, Number와 Number
    +new Number(10) + 0 === 10; // True, 타입을 자동으로 변환해주기 때문에 
    +

    new 키워드와 함께 Number 같은 기본 타입의 생성자를 호출하면 객체를 생성하지만 new 없이 호출하면 형 변환만 시킨다.

    +

    그리고 객체가 아니라 단순히 값이나 리터럴을 사용하면 타입 변환이 더 많이 일어난다.

    +

    가능한 정확하게 타입을 변환해주는 것이 최선이다.

    +

    스트링으로 변환하기

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

    숫자를 빈 스트링과 더하면 쉽게 스트링으로 변환할 수 있다.

    +

    숫자로 변환하기

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

    + 연산자만 앞에 붙여주면 스트링을 쉽게 숫자로 변환할 수 있다.

    +

    Boolean으로 변환하기

    +

    '!' 연산자를 두 번 사용하면 쉽게 Boolean으로 변환할 수 있다.

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

    핵심

    eval을 사용하면 안 될까?

    eval 함수는 JavaScript 문자열을 지역 스코프에서 실행한다.

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

    eval함수는 eval이라는 이름으로 직접 실행할 때에만 지역 스코프에서 실행된다. 그리고 eval이라는 이름에 걸맞게 악명또한 높다.

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

    어쨌든 eval은 사용하지 말아야 한다. eval을 사용하는 99.9%는 사실 eval 없이도 만들수있다.

    +

    가짜 eval

    +

    setTimeoutsetInterval은 첫 번째 인자로 스트링을 입력받을 수 있다. 이 경우에는 eval을 직접 호출하는 것이 아니라서 항상 Global Scope에서 실행된다.

    +

    보안 이슈

    +

    eval은 어떤 코드라도 무조건 실행하기 때문에 보안 문제도 있다. 따라서 신뢰하지 못하거나 모르는 코드가 포함되어 있을 경우 절대로 사용해서는 안된다.

    +

    결론

    +

    eval은 사용하지 않는 게 좋다. eval을 사용하는 모든 코드는 성능, 보안, 버그 문제를 일으킬 수 있다. 만약 eval이 필요해지면 설계를 변경하여 eval이 필요 없게 만들어야 한다.

    +

    undefinednull

    JavaScript는 nothing을 표현할때 nullundefined 두 가지로 표현할 수 있고 그중 undefined가 더 유용하다.

    +

    undefined도 변수

    +

    undefinedundefined라는 값을 가지는 데이터 형식이다.

    +

    undefined는 상수도 아니고 JavaScript의 키워드도 아니다. 그냥 undefined라는 이름의 Global 변수이고 이 변수에는 undefined라고 할당돼 있다. 그래서 이 Global 변수의 값을 쉽게 바꿀 수 있다.

    + +

    undefined 값이 반환될 때:

    +
      +
    • global 변수 undefined에 접근할 때.
    • +
    • 선언은 했지만 아직 초기화하지 않은 변수에 접근할 때.
    • +
    • return 구문이 없는 함수는 암묵적으로 undefined를 반환함.
    • +
    • return 구문으로 아무것도 반환하지 않을 때.
    • +
    • 없는 프로퍼티를 찾을 때.
    • +
    • 함수 인자가 생략될 때.
    • +
    • undefined가 할당된 모든 것.
    • +
    • void(expression) 형식으로 된 표현
    • +
    + +

    undefined가 바뀔 때를 대비하기

    +

    global 변수 undefinedundefined라는 객체를 가리키는 것뿐이기 때문에 새로운 값을 할당한다고 해도 undefined의 값 자체가 바뀌는 것이 아니다.

    +

    그래서 undefined와 비교하려면 먼저 undefined의 값을 찾아와야 한다.

    +

    undefined 변수가 바뀔 때를 대비해서 undefined라는 변수를 인자로 받는 anonymous wrapper로 감싸고 인자를 넘기지 않는 꼼수를 사용한다.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // Local Scope에 undefined를 만들어서
    +    // 원래 값을 가리키도록 했다.
    +
    +})('Hello World', 42);
    +

    wrapper 안에 변수를 새로 정의하는 방법으로도 같은 효과를 볼 수 있다.

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

    이 두 방법의 차이는 minified했을 때 4바이트만큼 차이 난다는 것과 한쪽은 wrapper 안에 var 구문이 없다는 것밖에 없다.

    +

    Null 객체의 용도

    +

    JavaScript 언어에서는 undefined를 다른 언어의 null 처럼 쓴다. 진짜 null은 그냥 데이터 타입 중 하나일 뿐이지 더도덜도 아니다.

    +

    JavaScript를 깊숙히 건드리는 것이 아니면 null 대신 undefined를 사용해도 된다(Foo.prototype = null같이 프로토타입 체인을 끊을 때는 null을 사용한다).

    +

    자동으로 삽입되는 쎄미콜론

    JavaScript는 C와 문법이 비슷하지만, 꼭 코드에 쎄미콜론을 사용하도록 강제하지는 않는다. 그래서 생략할 수 있다.

    +

    사실 JavaScript는 쎄미콜론이 꼭 있어야 하고 없으면 이해하지 못한다. 그래서 JavaScript 파서는 쎄미콜론이 없으면 자동으로 쎄미콜론을 추가한다.

    +
    var foo = function() {
    +} // 쎄미콜론이 없으니 에러 난다.
    +test()
    +

    파서는 쎄미콜론을 삽입하고 다시 시도한다.

    +
    var foo = function() {
    +}; // 에러가 없어짐.
    +test()
    +

    쎄미콜론을 자동으로 삽입한 것이 대표적인 JavaScript 설계 오류다. 쎄미콜론 유무에 따라 전혀 다른 코드가 될 수 있다.

    +

    어떻게 다를까?

    +

    코드에 쎄미콜론이 없으면 파서가 어디에 넣을지 결정한다.

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

    파서는 이 코드에 쎄미콜론을 다음과 같이 삽입한다.

    +
    (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); //<- 여기에 파서는 쎄미콜론을 넣는다.
    + +

    파서는 완전히 다른 코드로 만들어 버린다. 이것은 오류다.

    +

    괄호 해석

    +

    파서는 괄호에는 쎄미콜론을 넣지 않는다.

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

    그래서 다음과 같이 한줄로 코드를 바꾼다.

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

    이렇게 한줄로 바뀌면 log 함수가 함수를 반환할 가능성이 거의 없으므로 undefined is not a function이라는 TypeError가 발생한다.

    +

    결론

    +

    쎄미콜론은 반드시 사용해야 한다. 그리고 {}도 생략하지 않고 꼭 사용하는 것이 좋다. 한 줄밖에 안 되는 if / else 블럭에서도 꼭 사용해야 한다. 이 두 가지 규칙을 잘 지키면 JavaScript 파서가 잘못 해석하는 일을 미리 방지하고 코드도 튼튼해진다.

    +

    delete 연산자

    간단히 말해서 전역 변수와 전역 함수 그리고 DontDelete 속성을 가진 자바스크립트 객체는 삭제할 수 없다.

    +

    Global 코드와 Function 코드

    +

    전역이나 함수 스코프에 정의한 함수나 변수는 모두 Activation 객체나 전역 객체의 프로퍼티다. 이 프로퍼티는 모두 DontDelete 속성을 가진다. 전역이나 함수 코드에 정의한 변수와 함수는 항상 DontDelete 프로퍼티로 만들어지기 때문에 삭제될 수 없다:

    +
    // Global 변수:
    +var a = 1; // DontDelete가 설정된다.
    +delete a; // false
    +a; // 1
    +
    +// Function:
    +function f() {} // DontDelete가 설정된다.
    +delete f; // false
    +typeof f; // "function"
    +
    +// 다시 할당해도 삭제할 수 없다:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    명시적인(Explicit) 프로퍼티

    +

    다음 예제에서 만드는 프로퍼티는 delete할 수 있다. 이런 걸 명시적인(Explicit) 프로퍼티라고 부른다:

    +
    // Explicit 프로퍼티를 만든다:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    obj.xobj.yDontDelete 속성이 아니라서 delete할 수 있다. 하지만 다음과 같은 코드도 잘 동작하기 때문에 헷갈린다:

    +
    // IE를 빼고 잘 동작한다:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true - 진짜 Global 변수인지 확인하는 것
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    this가 전역 객체를 가리키는 것을 이용해서 명시적으로 프로퍼티 a를 선언하면 삭제할 수 있다. 이것은 꼼수다.

    +

    IE (적어도 6-8)는 버그가 있어서 안 된다.

    +

    Argument들과 Function의 기본 프로퍼티

    +

    Function의 arguments 객체와 기본 프로퍼티도 DontDelete 속성이다.

    +
    // Function의 arguments와 프로퍼티:
    +(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 객체

    + +

    Host 객체를 delete하면 어떻게 될지 알 수 없다. 표준에는 어떻게 Host 객체를 delete해야 하는지 정의하지 않았다.

    +

    결론

    +

    delete 연산자는 엉뚱하게 동작할 때가 많다. 명시적으로 정의한 일반 객체의 프로퍼티만 delete하는 것이 안전하다.

    +

    기타

    setTimeoutsetInterval

    JavaScript는 setTimeoutsetInterval함수를 이용해 비동기로 함수를 실행시킬수있다.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // 0보다 큰 수를 반환한다.
    +

    setTimeout을 호출하면 타이머의 ID를 반환하고 대략 1,000밀리 초 후에 foo를 실행시킨다. foo딱 한 번만 실행한다.

    +

    JS엔진은 타이머에 설정한 시간(timer resolution)에 따라서 코드를 실행하지만 단일 쓰레드이기 때문에 특정 코드는 실행이 지연 될수도 있다. 따라서 setTimeout으로 코드가 실행돼야 할 시간을 정해줘도 정확하게 그 시간에 실행되지 않을수도 있다..

    +

    첫 번째 인자로 넘긴 함수는 전역 객체가 실행시킨다. 따라서 인자로 넘겨진 함수 내부의 this전역 객체를 가리키게 된다.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this는 전역 객체를 가리키기 때문에 
    +        console.log(this.value); // undefined를 출력한다.
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    함수 호출을 쌓는(Stacking) setInterval함수.

    +

    setTimeout은 딱 한 번 함수를 호출하지만 setInterval은 이름처럼 지정한 시간마다 함수를 실행시켜준다. 하지만 이 함수의 사용은 좀 생각해봐야한다.

    +

    setInterval은 실행하는 코드가 일정시간 동안 블럭되도 계속해서 함수를 호출하기 때문에 주기가 짧은 경우 함수 호출이 쉽게 쌓여버린다.

    +
    function foo(){
    +    // 1초 동안 블럭함.
    +}
    +setInterval(foo, 100);
    +

    위 코드에서 foo함수는 호출될 때마다 1초씩 실행을 지연시킨다.

    +

    하지만 foo함수가 블럭되더라도 setInterval함수는 계속해서 함수 호출을 쌓기 때문에 foo함수 호출이 끝나면 10번 이상의 함수 호출이 쌓여서 대기하고 있을수도 있다. +(역주: 따라서 함수 호출이 쌓이게 되면 원래 기대했던 실행 주기를 보장받지 못한다.)

    +

    블럭되는 코드 해결법

    +

    앞에 문제를 해결하는 가장 쉽고 일반적인 방법은 setTimeout 함수에서 자기 자신을 다시 호출하는 방법이다.

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

    이 방법은 함수 호출이 쌓이지도 않을 뿐만 아니라 setTimeout 호출을 해당 함수 안에서 관리하기 때문에 foo 함수에서 계속 실행할지 말지도 조절할 수 있다.

    +

    타이머 없애기

    +

    clearTimeoutclearInterval 함수로 setTimeout과 setInterval로 등록한 timeout과 interval을 삭제할 수 있다. set 함수들이 반환한 id를 저장했다가 clear 함수를 호출해서 삭제한다.

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

    모든 타이머 없애기

    +

    등록한 timeout과 interval을 한꺼번에 제거하는 내장 함수는 없다. 따라서 좀 무식하지만 직접 구현해야 한다.

    +
    // "모든" 타이머 지우기
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    위와 같은 방법은 숫자가 미치지 못하는 타이머는 여전히 남아있을수 있다는 단점이 있다. 또 다른 해결 방법은 타이머가 반환하는 값이 항상 전보다 1만큼 큰 수를 반환한다는 점을 착안한 방법이다.

    +
    // "모든" 타이머 지우기
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    이 방법은 모든 주요 브라우저에서 문제없이 잘 동작하지만 ID가 항상 순차적이어야 한다고 표준에 명시된 것이 아니다. 그러므로 timeout ID를 모두 저장했다가 삭제하는 것이 가장 안전하다. 그러면 전부 깨끗하게 제거할 수 있다.

    +

    보이지 않게 사용되는 eval함수

    +

    setTimeoutsetInterval의 첫 파라미터로 문자열을 넘길 수 있다. 하지만 내부적으로 eval을 사용하는 것이기 때문에 절대 사용해서는 안된다.

    + +
    function foo() {
    +    // 이게 호출됨
    +}
    +
    +function bar() {
    +    function foo() {
    +        // 이것은 절대 호출 안 됨
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    이 경우 eval그냥(directly) 호출되는 것이 아니다. setTimeout에 인자로 넘어간 문자열은 전역 스코프에서 실행되기 때문에 bar함수 영역에 있는 지역 변수 foo가 실행되는 것이 아니라 전역 스코프에 있는 foo가 실행된다.

    +

    함수에 파라미터를 넘겨야 하면 스트링을 사용하지 말아야 한다.

    +
    function foo(a, b, c) {}
    +
    +// 절대 사용하면 안 됨
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// 대신 익명 함수를 사용하는 게 좋다.
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    결론

    +

    setTimeoutsetInterval함수에 문자열 인자를 절대 사용해서는 안된다. 핸들러 함수에 인자를 넘기는 코드도 절대 좋은 코드가 아니다. 익명 함수을 사용해서 호출해야 한다.

    +

    그리고 setInterval은 해당 핸들러가 블럭되든 말든 상관하지 않기 때문에 되도록이면 쓰지말자.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/pl/index.html b/external/JavaScript-Garden/pl/index.html new file mode 100644 index 0000000..6e0f602 --- /dev/null +++ b/external/JavaScript-Garden/pl/index.html @@ -0,0 +1,1440 @@ +JavaScript Garden

    Wstęp

    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ówki, 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 się 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 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
    +

    Istnieje kilka rozwiązań, dzięki 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 najpierw zewaluowane
    +

    Obiekty jako typy danych

    +

    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ść stworzenia obiektu prostego. +Ten nowy obiekt będzie dziedziczył z Object.prototype oraz +nie będzie posiadał żadnych własnych właściwości.

    +
    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 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 wyrzucenia +błędu podczas odczytu nieistniejącej właściwości.

    +

    Usuwanie właściwości

    +

    Jedynym sposobem na faktyczne 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': 'jestem słowem kluczowym, więc muszę być w cudzysłowie',
    +    delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError
    +};
    +

    Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów +lub apostrofów) lub jako string (w cudzysłowach lub apostrofach). +Ze względu na kolejne niedociągnięcie w parserze JavaScript, +powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript poniżej 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. 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 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óry 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.

    + +
    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', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    W powyższym przykładzie obiekt test będzie dziedziczył z obydwu, tj. +Bar.prototype i Foo.prototype, 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 jedyną instancją Foo i stała się jego prototypem. +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 bez nazwy.

    +

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

    +

    Rozszerzanie natywnych prototypów

    +

    Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z +najczęściej nadużywanej częsci języka JavaScript.

    +

    Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji. +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 się w nowszych silnikach JavaScript, np. Array.forEach

    +

    Wnioski

    +

    Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczenia,
    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órej wszystkie obiekty dziedziczą z Object.prototype.

    + +

    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
    +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 będzie 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 kontekstem 
    +// `this` ustawionym na foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    Wnioski

    +

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

    + +

    Filtrowania przy użyciu hasOwnProperty

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

    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 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 ani tego, czy +natywne prototypy zostały rozszerzone, czy nie.

    +

    Funkcje

    Deklaracje funkcji i wyrażenia funkcyjne

    Funkcje 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 funkcji

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

    +

    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 wskazywał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ć +traktowany 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 +funkcję, 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ęstszych 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łana 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ść this zostanie 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.

    +
      +
    1. Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod +poprzez prototyp.
    2. +
    3. 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.
    4. +
    5. Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe +new jest sprzeczne z duchem języka.
    6. +
    +

    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, 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. Brak użycia 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, +w przypadku kodu, który posiada wielu tysięcy linii, brak 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 modyfikacja
    zewnętrznego zasięgu.

    +

    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 zasięgu 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).

    +

    Analizując poniższy kod bez wiedzy na temat hoistingu możemy odnieść wrażenie, +że zobaczymy błąd ReferenceError.

    +
    // sprawdź, 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
    +
    +// upewnij się, ż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. +
    3. Jeżeli jeden z parametrów fukcji został nazwany foo, użyj go.
    4. +
    5. Jeżeli funkcja została nazwana foo, skorzystaj z tego.
    6. +
    7. Przejdź do zewnętrznego zasięgu i przejdź do kroku #1.
    8. +
    + +

    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 (inaczej: Immediately-Invoked +Function Expression - IIFE).

    +
    (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, jedynym sposobem, aby wykluczyć te właściwości, jest użycie +hasOwnProperty. Wówczas pętla staje się jednak +dwadzieścia 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 wyszukiwania tej właściwości przy każdej iteracji w pętli. +Najnowsze silniki JavaScript mogą zastosować w tym +przypadku optymalizację. Nie ma jednak możliwości 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 właściwością length ustawioną 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łędów.

    +

    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żsamość 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. Posiada on praktycznie
    same wady.

    +

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

    +
    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ć 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ę +typów gdziekolwiek jest to możliwe.

    +
    // 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 == '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 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 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 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.

    +

    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 się 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 przy użyciu funkcji setTimeout i setInterval.

    + +
    function foo() {}
    +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.

    +

    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 +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 wskazywać 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 kilkukrotnie. Może się to zdarzyć +szczególnie 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, +w kolejce do wywołania będzie już czekało kolejne dziesięć wywołań tej funkcji.

    +

    Radzenie sobie z możliwymi blokadami

    +

    Najprostszą, 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 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

    +

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

    +

    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 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 niestosowanie 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(1, 2, 3);
    +}, 1000)
    + +

    Wnioski

    +

    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ć korzystania z setInterval, ponieważ planista może +zablokować wykonanie JavaScriptu.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/ptbr/index.html b/external/JavaScript-Garden/ptbr/index.html new file mode 100644 index 0000000..946a360 --- /dev/null +++ b/external/JavaScript-Garden/ptbr/index.html @@ -0,0 +1,1365 @@ +JavaScript Garden

    Introdução

    Introdução

    JavaScript Garden é uma coletânea crescente que documenta as peculiaridades da linguagem de programação JavaScript. Nela você encontra recomendações para escapar dos erros comuns aos mais sutís, bem como de problemas de desempenho e práticas ruins, que programadores novatos podem acabar encontrando enquanto se aprofundam na linguagem.

    +

    JavaScript Garden não tem como propósito te ensinar à programar em JavaScript. Conhecimento prévio da linguagem é fortemente recomendado para que você entenda o conteúdo dos tópicos abordados neste guia. A fim de aprender as noções básicas da linguagem, por favor, consulte o excelente guia disponível na Mozilla Developer Network.

    +

    Os autores

    +

    Este guia é fruto do trabalho de dois excelentes usuários do Stack Overflow, Ivo Wetzel +(Conteúdo) e Zhang Yi Jiang (Design).

    +

    É mantido atualmente por Tim Ruffles.

    +

    Contribuidores

    + +

    Hospedagem

    +

    JavaScript Garden está hospedado no GitHub, porém Cramer Development nos apoia com um espelho em JavaScriptGarden.info.

    +

    Licença

    +

    JavaScript Garden está publicado sob a licença MIT e hospedado no GitHub. Caso você encontre defeitos ou erros de digitação, por favor registre o problema ou realize um pull request no repositório. Você também pode nos encontrar na sala de JavaScript no chat do Stack Overflow.

    +

    Objetos

    Propriedades e manipulação de objetos

    Tudo em JavaScript se comporta como um objeto, com apenas duas exceções que são +null e undefined.

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

    Um equívoco muito comum é a idéia de que números não podem ser manipulados como objetos. O parser do JavaScript analisa a notação de ponto como ponto flutuante de um número.

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

    Existem três soluções para contornar este problema e permtir que números se comportem como objetos.

    +
    2..toString(); // o segundo ponto é reconhecido corretamente
    +2 .toString(); // perceba o espaço deixado à esquerda do ponto
    +(2).toString(); // 2 é interpretado primeiro
    +

    Objetos como tipo de dados

    +

    Em JavaScript, Objetos podem também ser utilizados como Hashmaps; eles consistem principalmente de propriedades nomeadas, que apontam para valores.

    +

    Usando um objeto literal - notação do tipo {}- é possível criar um objeto simples. Este novo objeto herda de Object.prototype e não possui propriedades próprias definidas.

    +
    var foo = {}; // um novo objeto vazio
    +
    +// um novo objeto com uma propriedade 'test' populada com o valor 12
    +var bar = {test: 12}; 
    +

    Acessando propriedades

    +

    As propriedades de um objeto podem ser acessadas de duas maneiras, através da notação de ponto ou da notação de colchete.

    +
    var foo = {name: 'kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // Erro de sintaxe
    +foo['1234']; // funciona
    +

    Ambas as notações trabalham de forma quase idêntica, com a única diferença de que o colchete permite configuração dinâmica de propriedades e uso de propriedades nomeadas que de outra maneira levaria à erros de sintaxe.

    +

    Removendo propriedades

    +

    A única maneira de remover uma propriedade de um objeto é através do operador delete; definir uma propriedade como undefined ou null somente apaga o valor associado com tal propriedade, mas não remove a 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]);
    +    }
    +}
    +

    O código acima retorna tanto bar undefined quantofoo null - somente baz foi removido e, portanto, não saiu no output.

    +

    Notações de Keys

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

    Propriedades de objetos podem ser tanto representadas como caracteres simples bem como strings. Devido a outro engano do parser do JavaScript, o exemplo acima irá retornar SyntaxError remetendo ao ECMAScript 5.

    +

    Este erro decorre do fato de que 'apagar' é uma palavra reservada; por consequencia, deve ser representada como uma string literal a fim de garantir a interpretação correta pelas antigas engines de JavaScript.

    +

    Prototype

    JavaScript não dispõe de nenhum modelo clássico de herança; em vez disso, ele +faz uso do modelo prototypal.

    +

    Enquanto isto é considerado muitas vezes como sendo um dos pontos fracos do JavaScript, o modelo de herança prototypal é de fato muito mais poderoso do que o modelo clássico. +Por exemplo, isto torna relativamente trivial construir um modelo clássico +com base no modelo prototypal, enquanto que o contrário se verifica como uma tarefa mais difícil.

    +

    JavaScript é a única linguagem amplamente utilizada que apresenta um modelo de herança do tipo prototypal, +por isso pode levar algum tempo até que você se ajuste às diferenças entre os dois modelos.

    +

    A primeira grande diferença é que herança em JavaScript utiliza o conceito de cadeias prototype.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Apontar Bar's prototype para uma nava instância de Foo
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Tenha certeza de que Bar é o construtor atual
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // criar uma nova instância de bar
    +
    +// A cadeia prototype resultante
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    No código acima, o objeto test irá herdar de ambos Bar.prototype e +Foo.prototype; portanto, ele terá acesso à função method que foi definida em Foo. +Ele também terá acesso à propriedade value da única instância de Foo que é seu próprio prototype. +É importante perceber que new Bar() não cria uma nova instância de Foo, mas +reutiliza aquela associada ao prototype; assim, todas as intâncias Bar dividirão a +mesma propriedade value.

    + +

    Buscando propriedades

    +

    Ao acessar as propriedades de um objeto, JavaScript irá percorre a cadeia prototype +até o topo para encontrar a propriedade solicitada.

    +

    Caso atinja o topo da cadeia - denominada Object.prototype - e não encontre +a propriedade especificada, o valor undefined será retornado.

    +

    A propriedade Prototype

    +

    Enquanto a propriedade prototype é utilizada pela linguagem na contrução de cadeia de prototype, +ainda é possível associar qualquer valor dado a ele. No entanto, tipos primitivos serão +ignorados quando associados como prototype.

    +
    function Foo() {}
    +Foo.prototype = 1; // sem efeito
    +

    Atribuindo objetos, como demonstrado no exemplo anterior, irá funcionar, e permite +a criação dinâmica de cadeias prototype.

    +

    Performance

    +

    O tempo de pesquisa por propriedades que estão no topo da cadeia prototype +pode ter um impacto negativo na performance, principalmente em código +onde a performance é um fator crítico. Além disso, a busca por propriedades que não existem +também atravessa a cadeia prototype.

    +

    Além disso, ao interagir com propriedades de um objeto +cada propriedade na cadeia prototype será enumerada.

    +

    Estendendo Prototypes nativos

    +

    Uma prática ruim que é normalmente utilizada é a de estender Object.prototype ou qualquer outro prototype construído.

    +

    Esta técnica é denominada monkey patching e quebra o encapsulamento. +Mesmo utilizada por frameworks populars como Prototype, não existe mais razão +para poluir tipos built-in com funcionalidades adicionais fora de padrão.

    +

    A única boa razão existente para continuar estendendo um built-in prototype +é a de assegurar as novas funcionalidade de engines JavaScript modernas; por exemplo, Array.forEach.

    +

    Conclusão

    +

    É essencial entender o modelo de herança prototypal antes de escrever código complexo +que faço uso do mesmo. Além disso, tome cuidado com o tamanho da cadeia prototype em seu código +e a refatore caso necessário a fim de evitar futuros problemas de performance. A respeito do prototypes nativos, +estes nunca devem ser estendidos ao menos que seja para manter a compatibilidade com novas +características do JavaScript.

    +

    hasOwnProperty

    Para verificar se uma propriedade está definida no próprio objeto e não em outro lugar +da sua cadeia prototype, é necessário utilizar o método +hasOwnProperty o qual todos os objetos herdam de Object.prototype.

    + +

    hasOwnProperty é a única coisa em JavaScript a qual lida com propriedades e não percorre a cadeia prototype.

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

    Somente hasOwnProperty irá retornar o resultado correto e esperado; isto é +essencial quando se interage sobre propriedades de qualquer objeto. Não existe +outra maneira de verificar propriedades que não estejam definidas no próprio objeto, mas +em outro lugar na cadeia prototype.

    +

    hasOwnProperty como propriedade

    +

    JavaScript não protege o nome da propriedade hasOwnProperty; assim, se +existe a possibilidade de algum objeto possuir uma propriedade com este mesmo nome, +torna-se necessário utilizar um hasOwnProperty externo a fim de obter resultados corretos.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // sempre retorna false
    +
    +// Utiliza hasOwnProperty de outro objeto e o instancia com 'this' apontado para foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    +// Também é possível utilizar hasOwnProperty do Object
    +// prototype para este fim
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
    +

    Conclusão

    +

    O método hasOwnProperty é a única maneira confiável para verificar a existência da propriedade em um objeto. +É recomendado que hasOwnProperty seja utilizado em cada interação de um laço for in +a fim de evitar erros de extensão do prototype.

    +

    O laço for in

    Assim como o operador in, o laço for in percorre a cadeia prototype quando interage sobre as propriedades de um objeto.

    + +
    // Poluindo o Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // retorna ambos bar e moo
    +}
    +

    Uma vez que não é possível alterar o comportamento do laço for in por si só, faz-se necessário filtrar as propriedades do objeto durante o ciclo de repetição do laço; isso é feito usando o método hasOwnProperty do Object.prototype.

    + +

    Utilizando hasOwnProperty como filtro

    +
    // o mesmo foo utilizado anteriormente
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Esta é única forma correta de usar. Devido ao uso de hasOwnProperty, o exemplo irá retornar moo. Quando hasOwnProperty é deixado de lado, o código fica propenso a erros nos casos em que o prototype - por exemplo Object.prototype- tenha sido estendido.

    +

    Um framework largamente utilizado que estende o Object.prototype é Prototype. +Quando este framework é utilizado, laços for in que não utilizam +hasOwnProperty ficam desprotegidos contra erros.

    +

    Conclusão

    +

    Recomenda-se utilizar hasOwnProperty sempre. Nunca faça pressuposições sobre o ambiente em que o código está sendo executado, ou se os prototypes nativos foram estendidos ou não.

    +

    Funções

    Declaração de funções e expressões

    Funções em JavaScript são objetos de primeira classe. Isto significa que elas +podem ser tratadas como qualquer outro tipo. Um uso muito comum desta característica é +o de passar uma função anônima como uma callback para outra, talvez uma função assíncrona.

    +

    A declaração function

    +
    function foo() {}
    +

    A função acima sofrerá hoisting antes que a execução do programa se inicie; assim, +ela estará disponível em todo o escopo em que foi definida, até mesmo se for chamada antes de ter +sido definida no código.

    +
    foo(); // Funciona pois foo foi elevada para o topo do escopo
    +function foo() {}
    +

    A expressão function

    +
    var foo = function() {};
    +

    Este exemplo atribui uma função sem nome e anônima à variável foo.

    +
    foo; // 'undefined'
    +foo(); // dispara TypeError
    +var foo = function() {};
    +

    Devido ao fato de que var é uma declaração que eleva a definição da variável foo ao topo do escopo, esta sofrerá hoist e foo estará declarado logo que o script for executado.

    +

    Como atribuições só ocorrem em tempo de execução, o valor default de foo +será undefined antes que o código seja executado.

    +

    Expressão de uma função nomeada

    +

    Outro caso especial é a atribuição de funções nomeadas.

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

    Aqui, bar não está disponível fora do escopo, uma vez que a função se encontra atribuída +em foo; no entanto, dentro de bar, ela esta disponível. Isto ocorre devido ao fato de +como o name resolution funciona em JavaScript, o nome da função está sempre +disponível no escopo local da própria função.

    +

    Como funciona o this

    JavaScript tem uma concepção diferente sobre a que a palavra reservada this se refere da maioria das outras linguagens de programação. Existem exatamente cinco diferentes maneiras as quais os valores de this podem ser referenciados na linguagem.

    +

    O escopo Global

    +
    this;
    +

    Quando usando this no escopo global, ele simplesmente estará apontando para o objeto global.

    +

    Chamando uma função

    +
    foo();
    +

    Aqui, this irá referenciar novamente o objeto global.

    + +

    Chamando um método

    +
    test.foo(); 
    +

    Neste exemplo, this irá referenciar test.

    +

    Chamando um construtor

    +
    new foo(); 
    +

    Uma chamada de função que é precedida pela palavra chave new age como +um construtor. Dentro da função, this irá se referir +a um objeto recém criado.

    +

    Referência explícita do 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
    +

    Quando utiliza-se os métodos call ou apply de Function.prototype, o valor de +this dentro da função chamada irá referenciar explicitamente o primeiro argumento +correspondente na chamada da função.

    +

    Como resultado, no exemplo anterior o escopo original do método não é aplicado, e this +dentro de foo irá referenciar bar.

    + +

    Erros comuns

    +

    Embora a maioria destes casos façam sentido, o primeiro pode ser considerado +como um engano de concepção da linguagem, já que nunca se mostrou útil.

    +
    Foo.method = function() {
    +    function test() {
    +        // this referencia o objeto global
    +    }
    +    test();
    +};
    +

    Um erro comum é achar que this dentro de test referencia Foo; enquanto que, na realidade + não é isto que acontece.

    +

    Com a finalidade de acessar Foo de dentro de test, é necessário instanciar +uma variável global dentro do método para se referir à Foo.

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Utilize that no lugar de this aqui
    +    }
    +    test();
    +};
    +

    that trata-se de uma variável normal, porém é normalmente utilizada para referências externas de this. +Quando combinadas com closures, também podem ser utilizadas para repassar this como valor.

    +

    Atribuindo métodos

    +

    Outra coisa que não funciona em JavaScript é function aliasing, ou seja, +atribuir um método a uma variável.

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

    Devido ao primeiro caso, test se comportará como uma chamada de função; como consequencia, +o this dentro do método não apontará para someObject.

    +

    Enquanto que realizar binding do this pareça uma idéia ruim, no fundo, é o que faz a +herança prototypal funcionar.

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

    Quando os métodos da instância de Bar são chamados, o this faz referência +àquela mesma instância.

    +

    Closures e Referências

    Uma das caracterísricas mais poderosas do JavaScript é a possibilidade de usar closures. Quando usamos closures, definimos que um escopo sempre poderá acessar o escopo externo no qual foi definido. Uma vez que a única concepção de escopo em JavaScripe é function scope, todas as funções, por default, agem como closures.

    +

    Emulando variáveis privadas

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

    Aqui, Counter retorna duas closures: a função 'increment' bem como a função 'get'. Ambas as funções mantêm uma referência ao escopo de 'Counter' e, portanto, sempre mantêm o acesso à variável 'count' definida naquele escopo.

    +

    Por que variáveis privadas funcionam

    +

    Uma vez que não é possível referenciar ou atribuir escopos em JavaScript, não existe uma maneira de acessar a variável 'count' por fora. A única maneira de interagir com a variável é através das duas closures.

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

    O código acima não irá mudar a variável 'count' no escopo de 'Counter', uma vez que 'foo.hack' não foi definido naquele escopo. Neste caso, uma variável 'global' 'count' será criada ou substituida.

    +

    Closures dentro de laços

    +

    Um erro comum é utilizar closures dentro de laços, como se elas copiassem o valor da variável de indexação do laço.

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

    O exemplo acima não retornará os números '0' até '9', mas os número '10' dez vezes.

    +

    A função anônima mantêm uma referência para 'i'. No momento em que 'console.log' é chamado, 'o laço for' já encerrou a execução, e o valor '10' está atrbuído em 'i'.

    +

    Com a finalidade de se obter o comportamento esperado, é necessário criar uma cópia do valor de 'i'.

    +

    Evitando problemas de referência

    +

    Com a finalidade de copiar o valor da variável de indexação do laço, a melhor opção é utilizar um wrapper anônimo.

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

    A função anônima será chamada imediatamente com 'i' como seu primeiro argumento e receberá uma cópia do valor de 'i' como parâmetro de 'e'.

    +

    A função anônima que é passada para o 'setTimeout' agora possui uma referência a 'e', cujo os valores não são modificados pelo laço.

    +

    Não existe outra maneira de se obter este resultado, que não seja retornando uma função do wrapper anônimo que terá, então, o mesmo comportamento que o código acima.

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

    Há ainda uma outra maneira, usando .bind, que pode ligar argumentos e um contexto'this' a uma função. Isto se comporta como o código acima

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

    O objeto arguments

    Todo escopo de uma função em JavaScript tem acesso à variável especial arguments. +Esta variável armazena uma lista de todos os argumentos que foram passados para a função.

    + +

    O objeto arguments não é um Array. Enquanto que ele possui uma semântica +parecida com a de um array - a saber a propriedade length - ele não herda de Array.prototype +e é de fato um Object.

    +

    Devido a isto, não é possível usar os métodos padrões de array como push, +pop ou slice no arguments. Enquanto que a iteração com um simples for loop funciona bem, +é necessário convertê-lo para um Array a fim de usar os métodos padrões de Array.

    +

    Convertendo em um Array

    +

    O código abaixo irá retornar um novo Array contendo todos os elementos do +objeto arguments.

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

    Por este tipo de conversão ser lenta, seu uso em porções de código que apresentam performance crítica não é recomendado.

    +

    Passando argumentos

    +

    O código abaixo é a maneira recomendada de se passar argumentos de uma função para outra.

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

    Outro truque é o de usar ambos call e apply juntos para criar 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);
    +};
    +

    Parâmetros formais Formal Parameters and Arguments Indices

    +

    O objeto arguments cria funções getter e setter para suas propriedades, +bem como os parâmetros formais da função.

    +

    Como resultado, alterando o valor de um parâmetro formal também mudará o valor +da propriedade correspondente no objeto arguments, e vice versa.

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

    Mitos e verdades sobre performance

    +

    A única vez em que o objeto arguments não é criado é quando é declarado como um nome dentro de uma função +ou declarado como um de seus parâmetros formais. Não importa se ele é usado ou não.

    +

    Ambos getters e setters são sempre criados; desta maneira, usá-los não causa impacto +de performance, especialmente não em código do mundo real, onde existe mais de um simples +acesso às propriedades do objeto arguments.

    + +

    Entretando, existe um caso em que a performance é drasticamente reduzida +em engines modernas de JavaScript. Este caso é o uso de arguments.callee

    +
    function foo() {
    +    arguments.callee; // Faça alguma coisa com os objeto deta função
    +    arguments.callee.caller; // e o calling do objeto da função
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Would normally be inlined...
    +    }
    +}
    +

    Isto não somente acaba com possíveis ganhos de performance que resultariam de inlining, +mas também quebram o encapsulamento pois a função agora depende de uma chamada específica de contexto.

    +

    O uso de arguments.callee é fortemente desencorajado.

    + +

    Construtores

    Construtores em JavaScript ainda são diferentes de muitas outras linguagens. +Qualquer chamada a uma função que seja precedida pela palavra-chave new age como um cosntrutor.

    +

    Dentro do construtor - a função chamada - o valor de this se refere ao objeto recém criado. +O prototype deste novo objeto é definido como o prototype do objeto da função que foi +invocada como construtor.

    +

    Se a função chamada não possui um return statement explícito, então implicitamente +retornará o valor de this - o novo objeto.

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

    O código acima chama Foo como construtor e define o prototype do objeto recém criado +como Foo.prototype.

    +

    No caso de um return statement explícito, a função retorna o valor +especificado pelo statement, mas somente se o valor de retorno for um Object.

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // um novo objeto
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // o objeto retornado
    +

    Quando a palavra-chave new é omitida, a função não retornará o novo objeto.

    +
    function Foo() {
    +    this.bla = 1; // esta definida no escopo global do objeto
    +}
    +Foo(); // undefined
    +

    Enquanto que o exemplo acima pareça funcionar em alguns casos, devido +a maneira como this funciona em JavaScript, o objeto global +será usado como valor do this.

    +

    Fábricas

    +

    A fim de omitir a palavra-chave new, o construtor da função deve retornar um valor explicitamente.

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

    Ambas as chamadas a Bar retornam a mesma coisa, um objeto recém criado +que tem a propriedade chamada method, que é uma Closure.

    +

    Deve-se perceber que a chamada new Bar() não afeta o prototype do objeto retornado. +Enquanto o prototype é definido no objeto recém criado, Bar nunca retornará um novo objeto.

    +

    No exemplo acima, não existe diferença funcional entre o uso ou não de new.

    +

    Criando novos objetos por fábricas

    +

    É recomendado não usar new pois eventual o esquecimento de seu uso +pode levar à defeitos.

    +

    A fim de criar um novo objeto, deve-se utilizar uma fábrica e construir o novo objeto dentro desta fábrica.

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

    Enquanto que o código acima previne defeitos decorrentes do esquecimnto da palavra-chave new +e certamente utiliza-se de private variables de forma mais fácil, este apresenta algumas desvantagens:

    +
      +
    1. Utiliza mais memória desde que os objetos criados não compartilham métodos em um prototype.
    2. +
    3. A fim de implementar herença, a fábrica precisa copiar todos os métodos de um outro objeto ou colocar o outro objeto no prototype do novo objeto.
    4. +
    5. Quebrar a cadeia prototype somente por causa de esquecer eventualmente o new vai contra o que é proposto pela linguagem.
    6. +
    +

    Conclusão

    +

    Enquanto que a omissão do new origine defeitos, não é certamente uma razão para +quebrar a estrura prototype como um todo. No final, a melhor solução é sempre a que se adequa às necessidades de cada projeto. +O importante é utilizar de forma consistente o modelo de criação de objetos escolhido.

    +

    Escopos e Namespaces

    Embora o JavaScript lide bem com a sintaxe de duas chaves para definir blocos, ele não oferece suporte a escopos em blocos; por isso, +todo o restante da linguagem é definido em escopo de função.

    +
    function test() { // define escopo
    +    for(var i = 0; i < 10; i++) { // não define escopo
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    + +

    Também não existem namespaces distintos em JavaScript, o que significa que tudo é +automaticamente definido em um namespace globalmente compartilhado.

    +

    Cada vez que uma variável é referenciada, o JavaScript vai percorrer toda a hierarquia +de escopos até encontrá-la. Caso ele alcance o escopo global e ainda não tenha encontrado +a variável, ele lançará um ReferenceError.

    +

    A queda das variáveis globais

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

    O dois scripts acima não têm o mesmo efeito. O Script A define uma +variável chamada foo no escopo global, e o Script B define foo no +escopo atual.

    +

    Novamente, isto não é mesma coisa: emitir o uso de var tem várias implicações.

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

    Deixando o statement var de fora da função test faz com que o valor de test seja sobrescrito. +Enquanto que à primeira vista isto não pareça um problema, um script com milhares de linhas de código +que não utiliza var apresenta erros horríveis e bugs difíceis de serem detectados.

    +
    // escopo global
    +var items = [/* uma lista qualquer */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // escopo de subLoop
    +    for(i = 0; i < 10; i++) { // esquecendo do var statement
    +        // faça algo incrível!
    +    }
    +}
    +

    O loop externo terminará depois da primeira chamada para subLoop, uma vez que subLoop +sobrescreve o valor global i. Utilizar var no segundo for loop evitaria facilmente este problema. +O var statement nunca pode ser esquecido a não ser que o efeito desejado seja afetar o escopo externo.

    +

    Variáveis locais

    +

    A única fonte de variáveis locais em JavaScript são parâmetros de função +e variáveis declaradas via var statement.

    +
    // escopo global
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // escopo local da função test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    Enquanto que foo e i são variáveis locais dentro do escopo da função test, +a atribuição de bar irá substituir a variável global com o mesmo nome.

    +

    Hoisting

    +

    Javascript eleva declarações. Isto quer dizer que ambas declarações var +e function serão movidas para o topo do escopo ao qual pertencem.

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

    O código acima é modificado antes mesmo que seja executado. O JavaScript move + todos as declarações var assim como as de function, para o topo +do escopo mais próximo.

    +
    // declarações var são movidas aqui
    +var bar, someValue; // default para 'undefined'
    +
    +// as declarações de função também são movidas
    +function test(data) {
    +    var goo, i, e; // escopo de bloco ausente move essas variáveis p/ cá
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // falha com um TypeError uma vez que bar continua 'undefined'
    +someValue = 42; // atribuições não são afetadas pelo hoisting
    +bar = function() {};
    +
    +test();
    +

    A falta de delimitação de um escopo não somente moverá var statements para fora de loops + e seus blocos, isto também fará com que os testes de determinados if se tornem não-intuitivos.

    +

    No código original, embora o if statement pareça modificar a variável global + goo, ele modifica a variável local - depois que hoisting foi aplicado.

    +

    Por desconhecer o hoisting, pode-se suspeitar que o código abaixo lançaria um +ReferenceError.

    +
    // verifique se SomeImportantThing já foi inicializado
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Mas é claro, isto funciona devido ao fato de que var statement é movido para o topo +do escopo global.

    +
    var SomeImportantThing;
    +
    +// outro código pode inicializar SomeImportantThing aqui, ou não
    +
    +// tenha certeza de que isto foi inicializado
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Ordem da Resolução de Nomes

    +

    Todos os escopos em JavaScript, incluindo o escopo global, possuem o this + o qual faz referência ao objeto atual.

    +

    Escopos de funções também possuem o arguments, o qual contêm +os argumentos que foram passados para a função.

    +

    Por exemplo, ao tentar acessar a variável denominada foo dentro do escopo de uma função, JavaScript irá +procurar pela variável na seguinte ordem:

    +
      +
    1. No caso de haver var foo statement no escopo atual, use-a.
    2. +
    3. Se um dos parâmetros é denominado foo, use-o.
    4. +
    5. Se a própria função é denominada foo, use-a.
    6. +
    7. Vá para o escopo externo mais próximo e inicie do #1.
    8. +
    + +

    Namespaces

    +

    Um problema comum relacionado ao fato de dispor de apenas um namespace global é +a probabilidade de esbarrar com problemas onde nomes de variáveis coincidem. Em JavaScript, +este problema pode ser facilmente evitado com a ajuda de wrappers anônimos.

    +
    (function() {
    +    // um "namespace" autocontido
    +
    +    window.foo = function() {
    +        // closure exposta
    +    };
    +
    +})(); // execute a função imediatamente
    +

    Funções sem nome são consideradas expressões; a fim de ser referênciável, +elas devem ser avaliadas.

    +
    ( // avalie a função dentro dos parênteses
    +function() {}
    +) // e retorne o objeto de função
    +() // chama o resultado da avaliação
    +

    Existem outras maneiras para avaliar e chamar diretamente a expressão da função a qual, +enquanto que diferentes em sintaxe, comportam-se da mesma maneira.

    +
    // Alguns outros estilos para invocar diretamente ...
    +!function(){}()
    ++function(){}()
    +(function(){}());
    +// e assim por diante...
    +

    Conclusão

    +

    É recomendado sempre utilizar um wrapper anônimo para encapsular código em seu +próprio namespace. Isto não é somente uma maneira de se proteger contra conflitos de nomes, +como também contribui para melhor modularização de programas.

    +

    Adicionalmente, o uso de variáveis globais é considerado uma prática ruim. Qualquer +uso delas indica código mal escrito que tende à apresentar erros e apresenta manutenção complexa.

    +

    Arrays

    Iteração com Arrays e propriedades

    Embora arrays em JavaScript sejam objetos, não existem boas razões para utilizar +o for in loop. De fato, existem muitas boas razões para +evitar o uso de for in com arrays.

    + +

    Uma vez que o for in loop enumera todas as propriedades que estão na cadeia +prototype e visto que o único modo de excluir tais propriedades é por meio do uso +do hasOwnProperty, ele chega a ser vinte vezes +mais custoso que o uso normal do for loop.

    +

    Iteração

    +

    A fim de atingir a melhor performance ao interagir sobre arrays, +a melhor opção é utilizar o clássico for loop.

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

    Existe um detalhe importante no exemplo acima , que é o caching +do comprimento do array via l = list.length.

    +

    Embora a propriedade length esteja definida no próprio array, ainda existe +um trabalho extra ao executar a busca em cada iteração do array. +Enquanto que recentes engines JavaScript talvez implementem um otimização +para este caso, não existe uma maneira de saber quando o código será executado em uma +dessas novas engines.

    +

    De fato, deixando de lado o armazenamento em caching pode resultar em um loop duas vezes mais rápido +do que com o armazenamento em caching.

    +

    A propriedade length

    +

    Enquanto que o getter da propriedade length retorna o total de elementos +que estão contidos no array, o setter pode ser usado para truncar o array.

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

    Atribuir um valor de menor para length trunca o array. Por outro lado, incrementando +o valor de length cria um array esparso.

    +

    Conclusão

    +

    Para melhor performance, é recomendado o uso do for loop e o cache da propriedade +length. O uso do for in loop na iteração com array é um sinal de código mal escrito +e tendencioso a apresentar defeitos, além de ter performance ruim.

    +

    O construtor Array

    Uma vez que o construtor Array é ambíguo na forma como ele lida com seus parâmetros, +o uso da notação [] é fortemente recomendado ao criar novo arrays.

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

    Nos casos onde somente um argumento é passado para o construtor Array e quando o argumento é +um Number, o construtor retornará um novo array sem elementos com a propriedade length configurada de acordo com o valor do argumento. +É importante perceber que somente a propriedade length do novo array será configurada desta maneira; os índices do array não serão inicializados.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, o índice não foi definida
    +

    Ser capaz de definir o comprimento de um array antecipadamente é útil em poucos casos, +como ao replicar uma string, em que se evita o uso de um loop.

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

    Conclusão

    +

    O uso de literais é preferencial na inicialição de Arrays. São curtos, possuem uma sintaxe limpa, e contribuem para a legibilidade do código.

    +

    Tipos

    Igualdades e comparações

    JavaScript tem duas maneiras diferentes de comparar a igualdades entre valores de objetos.

    +

    O operador de igualdade

    +

    O operador de igualdade consiste de dois sinais de igual : ==

    +

    JavaScript é fracamente tipado. Isto que dizer que o operador de igualdade +induz tipos ao invés de compará-los.

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

    A tabela acima mostra o resultado da coerção de tipos, e isto é principal razão +para que o uso == seja amplamente considerado uma má prática. Seu uso introduz defeitos +difíceis de serem rastreados devido às suas complicadas regras de conversão.

    +

    Adicionalmente, também existe um impacto em performance quando a coerção acontece; +por exemplo, é necessário que uma string seja convertida em um número antes que seja comparada +com outro número.

    +

    O operador de igualdade estrito

    +

    O operador de igualdade estrito consiste de três sinais de igual : ===.

    +

    Ele funciona como o operador de igualdade normal, salvo que o operador de igualdade estrito +não realiza coerção de tipos entre seus operandos.

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

    Os resultados acima são bastante claros e permitem uma análise objetiva do código. Pode parecer complicar o código até um certo ponto + mas também traz ganhos de performance em casos em que os operandos são de tipos diferentes.

    +

    Comparando Objetos

    +

    Enquanto que ambos == e === são denominados operadores de igualdade, eles se comportam de +formas diferentes quando pelo menos um de seus operandos é um Object.

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

    Aqui, ambos os operadores comparam em função da identidade e não da igualdade; isto é, +eles vão comparar em função da mesma instância do objeto, muito parecido com o is do Python +e a comparação de ponteiros em C.

    +

    Conclusão

    +

    E fortemente recomendado que só se use o operador de igualdade estrito. +Em casos onde a coerção de tipos seja necessária, isto deve ser feito explicitamente +e não deve ser deixado para as complicadas regras de coerção da linguagem.

    +

    O operador typeof

    O operador typeof(em conjunto com +instanceof é provavelmente a maior falha de design do JavaScript, +por estar complemente mal implementado.

    +

    Embora instanceof ainda tenha seu uso limitado, typeof realmente só possui uma utilidade, +a qual não acaba por ser a de verificar o tipo de um objeto.

    + +

    A tabela de tipos em JavaScript

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

    Na tabela acima, Type se refere ao valor de retorno do operador typeof. +Como pode ser facilmente observado, este valor não é nada consistente.

    +

    O Class se refere ao valor interno da propriedade [[Class]] de um objeto.

    + +

    A fim de se obeter o valor de [[Class]], deve-se utilizar o método +toString de Object.prototype.

    +

    A classe de um objeto

    +

    A especificação fornece exatamente uma maneira de acessar o valor de [[Class]], +com o uso de 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
    +

    No exemplo acima, Object.prototype.toString é chamado enquanto que o valor de this +é definido como o objeto o qual o valor [[Class]] deva ser retornado.

    + +

    Teste para variáveis não-definidas

    +
    typeof foo !== 'undefined'
    +

    O exemplo acima irá verificar se foo foi declarado ou não; apenas +o fato de referênciá-lo poderia resultar em ReferenceError. Esta é a única utilidade +real de typeof.

    +

    Conclusão

    +

    A fim de verificar o tipo de um objeto, é fortemente recomendade o uso de +Object.prototype.toString pelo motivo de que esta é a única maneira confiável de ser feita. +Como demonstrado na tabela anterior, alguns valores retornados de typeof não estão definidos na +especificação; assim, eles podem variar entre implementações. +O uso de typeof deve ser evitado, a menos que não se esteja testando se uma variável está ou não definida.

    +

    O operador instanceof

    O operador instanceof compara os construtores de seus dois operandos. +Ele é útil somente quando estamos comparando objetos personalizados. Quando utilizado em tipos nativos, +ele é tão inútil quanto o operador typeof.

    +

    Comparando objetos personalizados

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Isto somente define Bar.prototype ao objeto de função Foo,
    +// mas não à instância atual de Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    Utilizando instanceof com tipos nativos

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

    Uma coisa importante para perceber aqui é que instanceof não funciona em objetos +originados de diferentes contextos de JavaScript (isto é, de diferentes documentos em um +navegador web), uma vez que seus construtores não irão representar exatamente o mesmo objeto.

    +

    Conclusão

    +

    O operador instanceof deve somente ser utilizado quando estive lidando +com objetos customizados originados de um mesmo contexto JavaScript. Bem como o operador +typeof, qualquer outro uso de instanceof deve ser evitado.

    +

    Conversão de tipos

    JavaScript é fracamente tipado, logo ele aplica a coerção de tipos +sempre que possível.

    +
    // Estes retornam true
    +new Number(10) == 10; // Number.toString() é convertido
    +                      // de volta a um número
    +
    +10 == '10';           // Strings são convertidas em Number
    +10 == '+10 ';         // Mais loucuras com strings
    +10 == '010';          // E mais 
    +isNaN(null) == false; // null é convertido em 0
    +                      // que claro não é NaN
    +
    +// Estes retornam false
    +10 == 010;
    +10 == '-10';
    + +

    A fim de evitar os problemas acima, o uso do operador de igualdade estritofortemente recomendado. Embora ele evite uma série de problemas comuns, +existem ainda muitas outras questões que surgem do fraco sistema de tipagem do JavaScript.

    +

    Construtores de tipos nativos

    +

    Os construtores de tipos nativos como Number e String comportam-se +diferentemente quando utilizados com ou sem a palavra-chave new.

    +
    new Number(10) === 10;     // False, Object e Number
    +Number(10) === 10;         // True, Number e Number
    +new Number(10) + 0 === 10; // True, devido à conversão implícita
    +

    Utilizar um tipo nativo como Number como construtor iré criar um novo objeto Number, +porém omitir a palavra-chave new fará com que a função Number se comporte como +um conversor.

    +

    Além, passando valores literais ou não-objetos irá resultar em mais coerções +de tipos.

    +

    A melhor opção é converter para um dos três possíveis tipos de forma explícita.

    +

    Convertendo para String

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

    Prefixando uma string vazia, qualquer valor pode ser facilmente convertido em uma string.

    +

    Convertendo para Number

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

    Ao utilizar o operador de soma unário, é possível converter um valor para Number.

    +

    Convertendo para Boolean

    +

    Ao utilizar duas vezes o operador not, é possível converter um valor para Boolean.

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

    Core

    Por que não utilizar eval

    A função eval executará uma string de código JavaScript no escopo local.

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

    Entretanto, eval somente é executado no escopo local quando é chamado diretamente +e quando o nome da função chamada é eval.

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

    O uso de eval deve ser evitado. 99.9% de seu "uso" pode ser alcançado sem ele.

    +

    eval dissimulado

    +

    As funções timeout setTimeout e setInterval podem ambas receberem uma string +como primeiro argumento. Tais strings sempre serão executadas no escopo global uma vez que +eval não é chamado diretamente, naquele caso.

    +

    Problemas de segurança

    +

    eval também é considerado um problema de segurança, por que executa qualquer código dado. +Ele nunca deve ser utilizado com strings de origens duvidosas ou desconhecidas.

    +

    Conclusão

    +

    eval nunca deve ser utilizado. Qualquer código que faça uso de eval seve ser questionado +em sua utilidade, performance e segurança. Se algo necessita de eval para funcionar, então não deve ser utilizado. +Um design melhor deve ser utilizado, um que não faça uso de eval.

    +

    undefined e null

    JavaScript tem duas formas distintas para representar o nada, null e undefined, com +o último sendo o mais útil.

    +

    O valor undefined

    +

    undefined é um tipo com um valor exato : undefined.

    +

    A linguagem também define uma variável global que tem como valor undefined; +esta variável também é chamada undefined. Entretanto tal variável não é nem uma constante +muito menos uma palavra-chave da linguagem. Isto significa que seu valor pode ser facilmente +sobrescrito.

    + +

    Aqui estão alguns exemplos de quando o valor undefined é retornado:

    +
      +
    • Acessando uma variável global (não-modificada) undefined.
    • +
    • Acessando uma variável declarada mas não ainda inicializada.
    • +
    • Retornos implícitos de funções devido ao esquecimento do return statement.
    • +
    • return statements que explicimente retornam o nada.
    • +
    • Busca por propriedades não-existentes.
    • +
    • Parâmetros de funções que não têm um valor explícito definido.
    • +
    • Qualquer coisa que tenha sido definida como undefined.
    • +
    • Qualquer expressão no formato void(expressão)
    • +
    +

    Manipulando mudanças no valor de undefined

    +

    Uma vez que a variável global undefined apenas mantém uma cópia do valor atual de undefined, atribuir-lhe +um novo valor não muda o valor do tipo undefined.

    +

    Ainda, a fim de comparar alguma coisa com o valor de undefined, é necessário que +primeiro se retorne o undefined.

    +

    A fim de proteger o código contra uma possível sobrescrtia da variável undefined, uma +técnica comum utilizada é a de adicionar um parâmetro adicional em um wrapper anônimo + que não recebe argumentos.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined no escopo local agora 
    +    // refer-se ao valor `undefined`
    +
    +})('Hello World', 42);
    +

    Outra maneira de atingir o mesmo efeito seria utilizar uma declaração dentro do wrapper.

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

    A única diferença aqui é a que a última versão resulta na redução de 4 bytes, e não existe +outro var statement dentro do wrapper anônimo.

    +

    Usos do null

    +

    Enquanto que undefined no contexto da linguagem JavaScript é normalmente utilizado +como um null, o atual null (ambos o tipo e o literal) é mais ou menos um outro tipo de dado.

    +

    Ele é utilizado internamente pelo JavaScript (como na declaração no fim da cadeia prototype +ao definir Foo.prototype = null), porém na maioria dos casos, pode ser substituido por undefined.

    +

    Inserção automática do ponto e vírgula

    Apesar do JavaScript possuir uma sintaxe no estilo C, o uso do ponto e vírgula não é obrigatório.

    +

    JavaScript não é uma linguagem 'semicolon-less'. De fato, o ponto e vírgula é necessário para o interpretação do código. Entretanto, o parser do JavaScript insere o ponto e vírgula automaticamente sempre que ocorrer um error de parser, decorrente da falta do ponto e vírgula.

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

    A inserção acontece e o parser realiza uma nova tentativa.

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

    A inseção automática de ponto e vírgula é considerada um dos maiores equívocos no design da linguagem pois pode influenciar no comportamento do código.

    +

    Como funciona

    +

    O código abaixo não possui ponto e vírgula, então fica à cargo do parser inserir o ponto e vírgula onde julgar necessário.

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

    Abaixo está o resultado do processamento do parser.

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

    O parser mudou o comportamento do código acima drásticamente. Em determinados casos, o parser não procede como o esperado.

    +

    Parênteses

    +

    No caso de parênteses, o parser não insere o ponto e vírgula.

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

    Este código é interpretado em uma só linha.

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

    As chances de log não retornar uma função são muito altas; portanto, o código acima irá produzir um TypeError informando que undefined is not a function.

    +

    Conclusão

    +

    É fortemente recomendado que nunca se omita o ponto e vírgula. Também é recomendado que chaves sejam mantidas na mesma linha que seus statements e que nunca sejam omitadas em declações de uma só linha como if / else statements. Tais medidas não somente melhorarão a consistência do código, como também irão previnir alteração no comportamento do código por má interpretação do parser do JavaScript.

    +

    O operador delete

    Em resumo, é impossível remover variáveis globais, funções e outras coisas em JavaScript +que tenham o atributo DontDelete definido.

    +

    Código global e código de função

    +

    Quando uma variável ou função é definida no escopo global ou em +um escopo de função ela passa a ser uma propriedade de ambos +objeto Activation e do objeto Global. Tais propriedades possuem um conjunto de atributos, um dos quais é o DontDelete. +Declarações de funções e variáveis em código global e em código de função +sempre criam propriedades com DontDelete, e portanto não podem ser removidas.

    +
    // variável global:
    +var a = 1; // DontDelete está definido
    +delete a; // false
    +a; // 1
    +
    +// função comum:
    +function f() {} // DontDelete está definido
    +delete f; // false
    +typeof f; // "function"
    +
    +// mudar o valor do atributo não ajuda:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    Propriedades explícitas

    +

    Propriedades definidas explicitamente podem ser apagadas normalmente.

    +
    // definição explícita de propriedade:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    No exemplo acima, obj.x e obj.y podem ser removidos por que eles não possuem o +atributo DontDelete. Este é o motivo pelo qual o exemplo abaixo também funciona.

    +
    // Desconsiderando o IE, isto funciona bem:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true - apenas uma variável global
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    Aqui nós utilizamos um truque para remover a. Aqui o this +faz referência ao objeto Global e declara explicitamente a variável a como +sua propriedade a qual nos permite removê-la.

    +

    O IE (pelo menos 6-8) possui defeitos, então o código acima não funciona.

    +

    Argumentos de função e propriedades nativas

    +

    Argumentos de função, objetos arguments e +propriedades nativas tambêm possuem o DontDelete definido.

    +
    // argumentos de funções e propriedades:
    +(function (x) {
    +
    +  delete arguments; // false
    +  typeof arguments; // "object"
    +
    +  delete x; // false
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // false
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    Objetos hosts

    +

    O comportamento do operador delete pode ser imprevisível para objetos hosts. +Devido a especificação, objetos hosts têm permissão para implementar qualquer tipo de comportamento.

    +

    Conclusão

    +

    O operador delete freqüentemente apresenta um comportamento inesperado e só +pode ser usado com segurança para remover propriedades definidas explicitamente em objetos normais.

    +

    Outros assuntos

    setTimeout e setInterval

    Uma vez que JavaScript é assíncrono, é possível agendar a execução de uma função +usando as funções setTimeout e setInterval.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // retorna um Number > 0
    +

    Quando setTimeout é chamado, ele retorna o ID do timeout e agenda a execução de foo +para aproximadamente mil milissegundos no futuro. +foo será executado uma única vez.

    +

    Dependendo de como a engine JavaScript que está rodando o código resolve o timer, bem como +o fato de que o JavaScript é single threaded e outro código que é executado pode bloquear a +thread, não há como garantir a precisão dos intervalos especificados nas chamadas setTimeout.

    +

    A função que foi passada como primeiro parâmetro será chamada pelo objeto global, o que +significa que o this dentro da função chamada se refere ao objeto global.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this faz referência ao objeto global
    +        console.log(this.value); // log undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Acumulando chamadas com o setInterval

    +

    Enquanto que setTimeout somente executa a função uma vez, setInterval - como +o nome sugere - irá executar a função a cada X milisegundos, porém seu uso é +desencorajado.

    +

    Quando um código em execução bloqueia a chamada do timeout, setInterval continuará +emitindo chamadas para a função em questão. Isto pode, especialmente com intervalos curtos, +resultar em uma pilha de chamadas de função.

    +
    function foo(){
    +    // algo que bloqueie por 1 segundo
    +}
    +setInterval(foo, 1000);
    +

    No código acima, foo será chamada uma vez e irá então bloquear a execução por um segundo.

    +

    Enquanto foo bloqueia a execução, setInterval irá programar mais chamadas para ela. +Em seguida, quando foo completar sua execução, existirão dez chamadas programadas +para ela aguardando por execução.

    +

    Lidando com possíveis bloqueios de código

    +

    A solução mais fácil, bem como a mais controlável, é usar setTimeout dentro da +própria função.

    +
    function foo(){
    +    // Algo que bloqueia por um segundo
    +    setTimeout(foo, 1000);
    +}
    +foo();
    +

    Isto não somente encapsula a chamada para setTimeout, mas também previne +o acumulo de chamadas e dá controle adicional. foo por si só pode decidir +quando rodar novamente ou não.

    +

    Limpando Timeouts manualmente

    +

    A limpeza de intervalos e timeouts funciona passando o respectivo ID +para clearTimeout ou clearInterval, dependendo onde a função set foi usada primeiro.

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

    Limpando todos os Timeouts

    +

    Como não existe métodos próprios para limpar todos os timeouts e/ou intervalos, +é necessário usar a força bruta para chegar a esta funcionalidade.

    +
    // limpe "todos" os timeouts
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Mas ainda podem haver timeouts que não serão afetados por este número arbitrário. +Uma outra maneira de fazer isto é considerar que o ID dado a um timeout é +incrementado um a um cada vez que você chama setTimeout.

    +
    // limpe "todos" os timeouts
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    Apesar desta maneira funcionar nos principais navegadores hoje em dia, não está especificado +que os IDs respeitem uma ordem como este, logo esta ordem pode ser variada. Por este motivo, em vez disso +é recomendade manter o controle de todos os IDs de timeouts, de forma que possam ser apagados precisamente.

    +

    O uso oculto do eval

    +

    setTimeout e setInterval aceitam uma string como primeiro argumento. +Esta funcionalidade nunca deve ser utilizada pois internamente faz uso de eval.

    + +
    function foo() {
    +    // será chamada
    +}
    +
    +function bar() {
    +    function foo() {
    +        // nunca será chamada
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Uma vez que eval não é chamado diretamente neste caso, a string +passada como argumento para setTimeout será executada no escopo global; assim, ela +não usará a variável local foo do escopo de bar.

    +

    Também é recomendado não usar uma string para passar argumentos +para a função que será chamada por qualquer uma das funções de timeout.

    +
    function foo(a, b, c) {}
    +
    +// NUNCA use isto
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// Utilize uma função anônima do lugar
    +setTimeout(function() {
    +    foo(a, b, c);
    +}, 1000)
    + +

    Conclusão

    +

    Uma string nunca deve ser usada como parâmetro setTimeout ou +setInterval. Esta prática é um sinal claro de código ruim, quando argumentos precisam ser fornecido para a função que é chamada. +Uma função anônima é que deve ser passada para que, em seguida, cuide da chamada.

    +

    Além disso, o uso de setInterval deve ser evitado pois seu scheduler não é +bloqueado pela execução do JavaScript.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/ru/index.html b/external/JavaScript-Garden/ru/index.html new file mode 100644 index 0000000..f8ce9c7 --- /dev/null +++ b/external/JavaScript-Garden/ru/index.html @@ -0,0 +1,1062 @@ +JavaScript Гарден

    Вступление

    Лицензия

    JavaScript Гарден распространяется под лицензией MIT и располагается на GitHub. Если вы найдёте ошибку или опечатку, пожалуйста сообщите нам о ней или запросите права на загрузку в репозиторий. Кроме того, вы можете найти нас в комнате JavaScript среди чатов Stack Overflow.

    +

    Объекты

    Объекты и их свойства

    В JavaScript всё ведет себя, как объект, лишь за двумя исключениями — null и undefined.

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

    Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение точечной нотации к числу воспринимается им как литерал числа с плавающей точкой.

    +
    2.toString(); // вызывает SyntaxError
    +

    Есть несколько способов обойти этот недостаток и любой из них можно использовать для того, чтобы работать с числами, как с объектами:

    +
    2..toString(); // вторая точка распознаётся корректно
    +2 .toString(); // обратите внимание на пробел перед точкой
    +(2).toString(); // двойка вычисляется заранее
    +

    Объекты как тип данных

    +

    Объекты в JavaScript могут использоваться как хеш-таблицы: подавляющей частью состоят из именованных свойств (ключей), привязанных к значениям.

    +

    Используя объектный литерал — нотацию {} — можно создать простой объект. Новый объект наследуется от Object.prototype и не имеет собственных свойств.

    +
    var foo = {}; // новый пустой объект
    +
    +// новый объект со свойством 'test', имеющим значение 12
    +var bar = {test: 12};
    +

    Доступ к свойствам

    +

    Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись квадратными скобками.

    +
    var foo = {name: 'kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // работает
    +

    Обе нотации идентичны по принципу работы — одна лишь разница в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке.

    +

    Удаление свойств

    +

    Единственный способ удалить свойство у объекта — использовать оператор delete; устанавливая свойство в undefined или null, вы только заменяете связанное с ним значение, но не удаляете ключ.

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

    Приведённый код выведет две строки: bar undefined и foo null — на самом деле удалено было только свойство baz и посему только оно будет отсутствовать в выводе.

    +

    Запись ключей

    +
    var test = {
    +    'case': 'Я — ключевое слово, поэтому меня надо записывать строкой',
    +    delete: 'Я тоже ключевое слово, так что я' // не является ошибкой, бросает SyntaxError только в версиях ECMAScript ниже 5ой версии
    +};
    +

    Свойства объектов могут записываться как явно символами, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит SyntaxError во всех версиях ранее ECMAScript 5.

    +

    Источником ошибки является факт, что delete — это ключевое слово и поэтому его необходимо записывать как строчный литерал: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript.

    +

    От перев.: И еще один пример в пользу строковой нотации, это относится к JSON:

    +
    // валидный JavaScript и валидный JSON
    +{
    +    "foo": "oof",
    +    "bar": "rab"
    +}
    +
    +// валидный JavaScript и НЕвалидный JSON
    +{
    +    foo: "oof",
    +    bar: "rab"
    +}
    +

    Великий Прототип

    В JavaScript отсутствует классическая модель наследования — вместо неё используется прототипная модель.

    +

    Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть.

    +

    Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями.

    +

    Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых цепочек прототипов.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype.method = function() {}
    +
    +function Bar() {}
    +
    +// Зададим наследование от Foo
    +Bar.prototype = Object.create(Foo.prototype);
    +Bar.prototype.foo = 'Hello World';
    +
    +// Убедимся, что Bar является действующим конструктором
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // создадим новый экземпляр bar
    +
    +// Цепочка прототипов, которая получится в результате
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* и т.д. */ }
    +

    В приведённом коде объект test наследует оба прототипа: Bar.prototype и Foo.prototype; следовательно, он имеет доступ к функции method которую мы определили в прототипе Foo. Также у него есть доступ к свойству value одного уникального экземпляра Foo, который является его прототипом. Важно заметить, что код new Bar() не создаёт новый экземпляр Foo, а повторно вызывает функцию, которая была назначена его прототипом: таким образом все новые экземпляры Bar будут иметь одинаковое свойство value.

    + +

    Поиск свойств

    +

    При обращении к какому-либо свойству объекта, JavaScript проходит вверх по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем.

    +

    Если он достигнет верхушки этой цепочки (Object.prototype) и при этом так и не найдёт указанное свойство, вместо него вернётся значение undefined.

    +

    Свойство prototype

    +

    То, что свойство prototype используется языком для построения цепочек прототипов, даёт нам возможность присвоить любое значение этому свойству. Однако обычные примитивы, если назначать их в качестве прототипа, будут просто-напросто игнорироваться.

    +
    function Foo() {}
    +Foo.prototype = 1; // ничего не произойдёт
    +Foo.prototype = {
    +    "foo":"bar"
    +};
    +

    При этом присвоение объектов, как в примере выше, позволит вам динамически создавать цепочки прототипов.

    +

    Производительность

    +

    Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями.

    +

    Вдобавок, при циклическом переборе свойств объекта, будет обработано каждое свойство, существующее в цепочке прототипов.

    +

    Расширение встроенных прототипов

    +

    Часто встречается неверное применение прототипов — расширение прототипа Object.prototype или прототипов одного из встроенных объектов JavaScript.

    +

    Подобная практика нарушает принцип инкапсуляции и имеет соответствующее название — monkey patching. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют.

    +

    Единственным оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функции Array.forEach, которая появилась в версии 1.6.

    +

    Заключение

    +

    Перед тем, как вы приступите к разработке сложных приложений на JavaScript, вы должны полностью осознать как работают прототипы, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того — никогда не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript).

    +

    Функция hasOwnProperty

    Если вам необходимо проверить, определено ли свойство у самого объекта, а не в его цепочке прототипов, вы можете использовать метод hasOwnProperty, который все объекты наследуют от Object.prototype.

    + +

    hasOwnProperty — единственная функция в JavaScript, которая позволяет получить свойства объекта без обращения к цепочке его прототипов.

    +
    // испортим 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
    +

    Только используя hasOwnProperty можно гарантировать правильный результат при переборе свойств объекта. И нет иного способа для определения свойств, которые определены в самом объекте, а не где-то в цепочке его прототипов.

    +

    hasOwnProperty как свойство

    +

    JavaScript не резервирует свойство с именем hasOwnProperty. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем, требуется использовать внешний вариант функции hasOwnProperty чтобы получить корректные результаты.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Да прилетят драконы'
    +};
    +
    +foo.hasOwnProperty('bar'); // всегда возвращает false
    +
    +// Используем метод hasOwnProperty пустого объекта
    +// и передаём foo в качестве this
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    Заключение

    +

    Единственным способом проверить существование свойства у объекта является использование метода hasOwnProperty. При этом рекомендуется использовать этот метод в каждом цикле for in вашего проекта, чтобы избежать возможных ошибок с ошибочным заимствованием свойств из прототипов родительских объектов. Также вы можете использовать конструкцию {}.hasOwnProperty.call(...) на случай, если кто-то вздумает расширить прототипы встроенных объектов.

    +

    Цикл for in

    Как и оператор in, цикл for in проходит по всей цепочке прототипов, обходя свойства объекта.

    + +
    // Испортим Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // печатает и bar и moo
    +}
    +

    Так как изменить поведение цикла for in как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используют метод hasOwnProperty из Object.prototype.

    + +

    Использование hasOwnProperty в качестве фильтра

    +
    // возьмём foo из примера выше
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Это единственная версия правильного использования цикла. Благодаря использованию hasOwnProperty будет выведено только свойство moo. Если же убрать hasOwnProperty, код становится нестабилен и могут возникнуть ошибки, особенно если кто-то изменил встроенные прототипы, такие как Object.prototype.

    +

    Один из самых популярных фреймворков Prototype как раз этим и славится, и если вы его подключаете, то не забудьте использовать hasOwnProperty внутри цикла for in, иначе у вас гарантированно возникнут проблемы.

    +

    Рекомендации

    +

    Рекомендация одна — всегда используйте hasOwnProperty. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека.

    +

    Функции

    Выражения и объявление функций

    Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача анонимной функции как функции обратного вызова в другую функцию — к примеру, для асинхронных вызовов.

    +

    Объявление function

    +
    // всё просто и привычно
    +function foo() {}
    +

    В следующем примере описанная функция резервируется перед запуском всего скрипта; за счёт этого она доступна в любом месте кода, вне зависимости от того, где она определена — даже если функция вызывается до её фактического объявления в коде.

    +
    foo(); // сработает, т.к. функция будет создана до выполнения кода
    +function foo() {}
    +

    function как выражение

    +
    var foo = function() {};
    +

    В этом примере безымянная и анонимная функция присваивается переменной foo.

    +
    foo; // 'undefined'
    +foo(); // вызовет TypeError
    +var foo = function() {};
    +

    Так как в данном примере выражение var — это определение функции, переменная с именем foo будет заранее зарезервирована перед запуском скрипта (таким образом, foo уже будет определена во время его работы).

    +

    Но поскольку присвоения исполняются непосредственно во время работы кода, foo по умолчанию будет присвоено значение undefined (до обработки строки с определением функции):

    +
    var foo; // переменная неявно резервируется
    +foo; // 'undefined'
    +foo(); // вызовет TypeError
    +foo = function() {};
    +

    Выражения с именованными фунциями

    +

    Существует еще нюанс, касающийся именованных функций создающихся через присваивание:

    +
    var foo = function bar() {
    +    bar(); // работает
    +}
    +bar(); // получим ReferenceError
    +

    Здесь объект bar не доступен во внешней области, так как имя bar используется только для присвоения переменной foo; однако bar можно вызвать внутри функции. Такое поведение связано с особенностью работы JavaScript с пространствами имен - имя функции всегда доступно в локальной области видимости самой функции.

    +

    Как работает this

    В JavaScript область ответственности специальной переменной this концептуально отличается от того, за что отвечает this в других языках программирования. Различают ровно пять вариантов того, к чему привязывается this в языке.

    +

    1. Глобальная область видимости

    +
    this;
    +

    Когда мы используем this в глобальной области, она будет просто ссылаться на глобальный объект.

    +

    2. Вызов функции

    +
    foo();
    +

    Тут this также ссылается на глобальный объект.

    + +

    3. Вызов метода

    +
    test.foo();
    +

    В данном примере this ссылается на test.

    +

    4. Вызов конструктора

    +
    new foo();
    +

    Если перед вызовом функции присутствует ключевое слово new, то данная функция будет действовать как конструктор. Внутри такой функции this будет указывать на новосозданный Object.

    +

    5. Переопределение this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3
    +foo.call(bar, 1, 2, 3); // аналогично
    +

    Когда мы используем методы call или apply из Function.prototype, то внутри вызываемой функции this явным образом будет присвоено значение первого передаваемого параметра.

    +

    Исходя из этого, в предыдущем примере (строка с apply) правило #3 вызов метода не будет применено, и this внутри foo будет присвоено bar.

    + +

    Наиболее распространенные ошибки

    +

    Хотя большинство из примеров ниже наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он вообще не имеет практического применения.

    +
    Foo.method = function() {
    +    function test() {
    +        // this ссылается на глобальный объект
    +    }
    +    test();
    +};
    +

    Распространенным заблуждением будет то, что this внутри test ссылается на Foo, но это не так.

    +

    Для того, чтобы получить доступ к Foo внутри функции test, необходимо создать локальную переменную внутри method, которая и будет ссылаться на Foo.

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Здесь используем that вместо this
    +    }
    +    test();
    +};
    +

    Подходящее имя для переменной - that, его часто используют для ссылки на внешний this. В комбинации с замыканиями this можно пробрасывать в глобальную область или в любой другой объект.

    + +

    Назначение методов

    +

    Еще одной фичей, которая не работает в JavaScript, является создание псевдонимов для методов, т.е. присвоение метода объекта переменной.

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

    Следуя первому правилу test вызывается как обычная функция; следовательно this внутри него больше не ссылается на someObject.

    +

    Хотя позднее связывание this на первый взгляд может показаться плохой идеей, но на самом деле именно благодаря этому работает наследование прототипов.

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

    В момент, когда будет вызван method нового экземпляра Bar, this будет ссылаться на этот самый экземпляр.

    +

    Замыкания и ссылки

    Одним из самых мощных инструментов JavaScript'а считаются возможность создавать замыкания — это такой приём, когда наша область видимости всегда имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это функции: т.о. объявляя функцию, вы автоматически реализуете замыкания.

    +

    Эмуляция приватных свойств

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

    В данном примере Counter возвращает два замыкания: функции increment и get. Обе эти функции сохраняют ссылку на область видимости Counter и, соответственно, имеют доступ к переменной count из этой самой области.

    +

    Как это работает

    +

    Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить count извне не представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий.

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

    В приведенном примере мы не изменяем переменную count в области видимости Counter, т.к. foo.hack не объявлен в данной области. Вместо этого будет создана или перезаписана глобальная переменная count;

    +

    Замыкания внутри циклов

    +

    Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь.

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

    Данный код не будет выводить числа с 0 до 9, вместо этого число 10 будет выведено десять раз.

    +

    Анонимная функция сохраняет ссылку на i и, когда будет вызвана функция console.log, цикл for уже закончит свою работу, а в i будет содержаться 10.

    +

    Для получения желаемого результата необходимо создать копию переменной i.

    +

    Во избежание ошибок

    +

    Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать анонимную функцию как обёртку.

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

    Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит i, значение которой будет скопировано в параметр e.

    +

    Анонимная функция, которая передается в setTimeout, теперь содержит ссылку на e, значение которой не изменяется циклом.

    +

    Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера.

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

    Объект arguments

    В области видимости любой функции в JavaScript есть доступ к специальной переменной arguments. Эта переменная содержит в себе список всех аргументов, переданных данной функции.

    + +

    Объект arguments не является наследником Array. Он, конечно же, очень похож на массив и даже содержит свойство length — но он не наследует Array.prototype, а представляет собой Object.

    +

    По этой причине, у объекта arguments отсутствуют стандартные методы массивов, такие как push, pop или slice. Хотя итерация с использованием обычного цикла for по аргументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа Array, чтобы применять к нему стандартные методы массивов.

    +

    Конвертация в массив

    +

    Указанный код вернёт новый массив типа Array, содержащий все элементы объекта arguments.

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

    Эта конвертация занимает много времени и использовать её в критических частях кода не рекомендуется.

    +

    Передача аргументов

    +

    Ниже представлен рекомендуемый способ передачи аргументов из одной функции в другую.

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // делаем здесь что-нибудь
    +}
    +

    Другой трюк — использовать и call и apply вместе, чтобы быстро создать несвязанную обёртку:

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Создаём несвязанную версию "method"
    +// Она принимает параметры: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Результат: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +
    +};
    +

    Формальные аргументы и индексы аргументов

    +

    Объект arguments создаёт по геттеру и сеттеру и для всех своих свойств и для формальных параметров функции.

    +

    В результате, изменение формального параметра также изменит значение соответствующего свойства объекта arguments и наоборот.

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

    Мифы и правда о производительности

    +

    Объект arguments создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет.

    +

    Геттеры и сеттеры создаются всегда; так что их использование практически никак не влияет на производительность.

    + +

    Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование arguments.callee.

    +
    function foo() {
    +    arguments.callee; // сделать что-либо с этим объектом функции
    +    arguments.callee.caller; // и с вызвавшим его объектом функции
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // При обычных условиях должна бы была быть развёрнута...
    +    }
    +}
    +

    В коде выше, функция foo не может быть развёрнута (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова.

    +

    Крайне не рекомендуется использовать arguments.callee или какое-либо из его свойств. Никогда.

    + +

    Конструктор

    Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова new, будет конструктором.

    +

    Внутри конструктора (вызываемой функции) this будет указывать на новосозданный Object. Прототипом этого нового объекта будет prototype функции, которая была вызвана в качестве конструктора.

    +

    Если вызываемая функция не имеет явного возврата посредством return, то вернётся this — этот новый объект.

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

    В этом примере Foo вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к Foo.prototype.

    +

    В случае, когда функция в явном виде возвращает некое значение используя return, то в результате выполнения конструктора мы получим именно его, но только если возвращаемое значение представляет собой Object.

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // новый объект
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // возвращённый объект
    +

    Если же опустить ключевое слово new, то функция не будет возвращать никаких объектов.

    +
    function Foo() {
    +    this.bla = 1; // устанавливается глобальному объекту
    +}
    +Foo(); // undefined
    +

    Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением this в JavaScript — он будет восприниматься парсером как глобальный объект.

    +

    Фабрики

    +

    Если хотите избавится от необходимости использования new, напишите конструктор, возвращающий значение посредством return.

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

    В обоих случаях при вызове Bar мы получим один и тот же результат — новый объект со свойством method (спасибо замыканию за это).

    +

    Также следует заметить, что вызов new Bar() никак не связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но Bar никогда не возвращает этот новый объект.

    +

    В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором new или без него.

    +

    Создание объектов с использованием фабрик

    +

    Часто не рекомендуют использовать new, поскольку если вы его забудете, это может привести к ошибкам.

    +

    Чтобы создать новый объект, лучше использовать фабрику и создать новый объект внутри этой фабрики.

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

    Хотя данный пример и сработает, если вы забыли ключевое слово new, и благодаря ему легче работать с приватными переменными, у него есть несколько недостатков

    +
      +
    1. Он использует больше памяти, поскольку созданные объекты не хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода.
    2. +
    3. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый.
    4. +
    5. Разрыв цепочки прототипов просто по причине забытого ключевого слова new идёт вразрез с духом языка.
    6. +
    +

    Заключение

    +

    Хотя забытое ключевое слово new и может привести к багам, это точно не причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого не изменять ему.

    +

    Области видимости и пространства имён

    Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он не поддерживает блочную область видимости; всё что остаётся на этот случай в языке — область видимости функций.

    +
    function test() { // область видимости
    +    for(var i = 0; i < 10; i++) { // не область видимости
    +        // считаем
    +    }
    +    console.log(i); // 10
    +}
    + +

    Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в глобально доступном пространстве имён.

    +

    Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он ругнётся ReferenceError.

    +

    Проклятие глобальных переменных

    +
    // скрипт A
    +foo = '42';
    +
    +// скрипт B
    +var foo = '42'
    +

    Вышеприведённые два скрипта не приводят к одному результату. Скрипт A определяет переменную по имени foo в глобальной области видимости, а скрипт B определяет foo в текущей области видимости.

    +

    Повторимся, это вообще не тот же самый эффект. Если вы не используете var — то вы в большой опасности.

    +
    // глобальная область видимости
    +var foo = 42;
    +function test() {
    +    // локальная область видимости
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    Из-за того что оператор var опущен внутри функции, фунция test перезапишет значение foo. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете var, то вам на пути встретятся страшные и трудноотлаживаемые ошибки — и это не шутка.

    +
    // глобальная область видимости
    +var items = [/* какой-то список */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // область видимости subLoop
    +    for(i = 0; i < 10; i++) { // пропущенный оператор var
    +        // делаем волшебные вещи!
    +    }
    +}
    +

    Внешний цикл прекратит работу сразу после первого вызова subLoop, поскольку subLoop перезаписывает глобальное значение переменной i. Использование var во втором цикле for могло бы вас легко избавить от этой ошибки. Никогда не забывайте использовать var, если только влияние на внешнюю область видимости не является тем, что вы намерены получить.

    +

    Локальные переменные

    +

    Единственный источник локальных переменных в JavaScript - это параметры функций и переменные, объявленные с использованием оператора var.

    +
    // глобальная область видимости
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // локальная область видимости для функции test
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    В то время как foo и i — локальные переменные в области видимости функции test, присвоение bar переопределит значение одноимённой глобальной переменной.

    +

    Всплытие

    +

    В JavaScript действует механизм всплытия определения. Это значит, что оба определения с использованием var и определение function будут перенесены наверх заключающей их области видимости.

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

    Этот код трансформируется ещё перед исполнением. JavaScript перемещает операторы var и определение function наверх ближайшей оборачивающей области видимости.

    +
    // выражения с var переместились сюда
    +var bar, someValue; // по умолчанию - 'undefined'
    +
    +// определение функции тоже переместилось
    +function test(data) {
    +    var goo, i, e; // потерянная блочная область видимости
    +                   // переместилась сюда
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // вылетает с ошибкой TypeError,
    +       // поскольку bar всё ещё 'undefined'
    +someValue = 42; // присвоения не подвержены всплытию
    +bar = function() {};
    +
    +test();
    +

    Потерянная область видимости блока не только переместит операторы var вовне циклов и их тел, но и сделает результаты некоторых конструкций с if неинтуитивными.

    +

    В исходном коде оператор if изменял глобальную переменную goo, когда, как оказалось, он изменяет локальную переменную — в результате работы всплытия.

    +

    Если вы не знакомы со всплытием, то можете посчитать, что нижеприведённый код должен породить +ReferenceError.

    +
    // проверить, проинициализована ли SomeImportantThing
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Но, конечно же, этот код работает: из-за того, что оператор var был перемещён наверх глобальной области видимости

    +
    var SomeImportantThing;
    +
    +// другой код может инициализировать здесь переменную SomeImportantThing,
    +// а может и нет
    +
    +// убедиться, что она всё ещё здесь
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    Порядок разрешения имён

    +

    Все области видимости в JavaScript, включая глобальную области видимости, содержат специальную, определённую внутри них, переменную this, которая ссылается на текущий объект.

    +

    Области видимости функций также содержат внутри себя переменную arguments, которая содержит аргументы, переданные в функцию.

    +

    Например, когда JavaScript пытается получить доступ к переменной foo в области видимости функции, он будет искать её по имени в такой последовательности:

    +
      +
    1. Если в текущей области видимости есть выражение var foo, использовать его.
    2. +
    3. Если один из параметров функции называется foo, использовать его.
    4. +
    5. Если функция сама называется foo, использовать её.
    6. +
    7. Перейти на одну область видимости выше и начать с п. 1
    8. +
    + +

    Пространства имён

    +

    Нередкое последствие наличия только одного глобального пространства имён — проблема с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя анонимные обёртки.

    +
    (function() {
    +    // самостоятельно созданное "пространство имён"
    +
    +    window.foo = function() {
    +        // открытое замыкание
    +    };
    +
    +})(); // сразу же выполнить функцию
    +

    Безымянные функции являются выражениями; поэтому, чтобы вы имели возможность их выполнить, они сперва должны быть разобраны.

    +
    ( // разобрать функцию внутри скобок
    +function() {}
    +) // и вернуть объект функции
    +() // вызвать результат разбора
    +

    Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, но действуют одинаково.

    +
    // Два других способа
    ++function(){}();
    +(function(){}());
    +

    Заключение

    +

    Рекомендуется всегда использовать анонимную обёртку для заключения кода в его собственное пространство имён. Это не только защищает код от совпадений имён, но и позволяет создавать более модульные программы.

    +

    Важно добавить, что использование глобальных переменных считается плохой практикой. Любое их использование демонстрирует плохое качество кода и может привести к трудноуловимым ошибкам.

    +

    Массивы

    Итерации по массивам и свойства

    Несмотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования цикла for in для итерации по элементам массива. Фактически, существует несколько весомых причин против использования for in в массивах.

    + +

    Во время выполнения for in циклически перебираются все свойства объекта, находящиеся в цепочке прототипов. Единственный способ исключить ненужные свойства — использовать hasOwnProperty, а это в 20 раз медленнее обычного цикла for.

    +

    Итерирование

    +

    Для достижения лучшей производительности при итерации по массивам, лучше всего использовать обычный цикл for.

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

    В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: l = list.length.

    +

    Несмотря на то, что свойство length определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Пусть в этом случае новые движки JavaScript теоретически могут применить оптимизацию, но нет никакого способа узнать, будет оптимизирован код на новом движке или нет.

    +

    Фактически, отсутствие кэширования может привести к выполнению цикла в два раза медленнее, чем при кэшировании длины

    +

    Свойство length

    +

    Хотя геттер свойства length просто возвращает количество элементов содержащихся в массиве, сеттер можно использовать для обрезания массива.

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

    Присвоение свойству length меньшей величины урезает массив, однако присвоение большего значения не даст никакого эффекта.

    +

    Заключение

    +

    Для оптимальной работы кода рекомендуется всегда использовать простой цикл for и кэшировать свойство length. Использование for in с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения.

    +

    Конструктор Array

    Так как в конструкторе Array есть некоторая двусмысленность, касающаяся его параметров, настоятельно рекомендуется при создании массивов всегда использовать синтаксис литеральной нотации — [].

    +
    [1, 2, 3]; // Результат: [1, 2, 3]
    +new Array(1, 2, 3); // Результат: [1, 2, 3]
    +
    +[3]; // Результат: [3]
    +new Array(3); // Результат: []
    +new Array('3') // Результат: ['3']
    +

    В случае, когда в конструктор Array передаётся один аргумент и этот аргумент имеет тип Number, конструктор возвращает новый, заполненный случайными значениями, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство length нового массива, индексы массива фактически не будут проинициализированы.

    +
    var arr = new Array(3);
    +arr[1]; // не определён, undefined
    +1 in arr; // false, индекс не был установлен
    +

    Поведение, которое позволяет изначально установить только размер массива, может пригодиться лишь в нескольких случаях, таких как повторение строк, за счёт чего избегается использование цикла for.

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

    Заключение

    +

    Использование конструктора Array нужно избегать, насколько это возможно. Литералы определённо предпочтительнее — это краткая запись и она имеет более понятный синтаксис, так что при этом даже улучшается читабельность кода.

    +

    Типы

    Равенство и сравнение

    JavaScript имеет 2 различных способа сравнения значений объектов на равенство.

    +

    Оператор сравнения

    +

    Оператор сравнения состоит из двух символов равенства: ==

    +

    Слабая типизированность языка JavaScript подразумевает приведение обеих переменных к одному типу для того, чтобы произвести сравнение.

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

    В таблице выше показаны результаты приведения типов и это главная причина, почему использование == повсеместно считается плохой практикой: оно приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов.

    +

    Кроме того, приведение типов во время сравнения также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом.

    +

    Оператор строгого равенства

    +

    Оператор строгого равенства состоит из трёх символов равенства: ===

    +

    В отличие от обычного оператора равенства, оператор строгого равенства не выполняет приведение типов между операндами.

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

    Результаты выше более понятны и позволяют быстрее выявлять ошибки в коде. Это в определённой степени улучшает код, а также дает прирост производительности в случае, если операнды имеют различные типы.

    +

    Сравнение объектов

    +

    Хотя оба оператора == и === заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы один из операндов является Object.

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

    Здесь оба операнда сравниваются на идентичность, а не на равенство; то есть будет проверяться, являются ли операнды одним экземпляром объекта, так же как делает is в Python и сравниваются указатели в С.

    +

    Заключение

    +

    Крайне рекомендуется использовать только операторы строгого равенства. В случае, когда намечается преобразование типов, нужно сделать явное приведение и не оставлять их на совести языковых хитростей с преобразованиями.

    +

    Оператор typeof

    Оператор typeof (вместе с instanceof) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он поломан более, чем полностью.

    +

    Хотя instanceof еще имеет ограниченное применение, typeof на самом деле имеет только один практический случай применения, который при всём при этом не является проверкой типа объекта.

    + +

    Таблица типов JavaScript

    +
    Значение            Класс      Тип
    +-------------------------------------
    +"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 в Nitro/V8)
    +new RegExp("meow")  RegExp     object (function в Nitro/V8)
    +{}                  Object     object
    +new Object()        Object     object
    +

    В таблице выше Тип представляет собой значение, возвращаемое оператором typeof. Как хорошо видно, это значение может быть абсолютно любым, но не логичным результатом.

    +

    Класс представляет собой значение внутреннего свойства [[Class]] объекта.

    + +

    Для того, чтобы получить значение [[Class]], необходимо вызвать метод toString у Object.prototype.

    +

    Класс объекта

    +

    Спецификация предоставляет только один способ доступа к значению [[Class]] — используя 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
    +

    В примере выше Object.prototype.toString вызывается со значением this, являющимся объектом, значение [[Class]] которого нужно получить.

    + +

    Проверка переменных на определённость

    +
    typeof foo !== 'undefined'
    +

    Выше проверяется, было ли foo действительно объявлено или нет; просто обращение к переменной приведёт к ReferenceError. Это единственное, чем на самом деле полезен typeof.

    +

    Заключение

    +

    Для проверки типа объекта настоятельно рекомендуется использоватьObject.prototype.toString — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые typeof значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях.

    +

    Кроме случая проверки, была ли определена переменная, typeof следует избегать во что бы то ни стало.

    +

    Оператор instanceof

    Оператор instanceof сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как и оператор typeof.

    +

    Сравнение пользовательских объектов

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Всего лишь присваиваем Bar.prototype объект функции Foo,
    +// но не экземпляра Foo
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    Использование instanceof со встроенными типами

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

    Здесь надо отметить одну важную вещь: instanceof не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы и правда не будут конструкторами тех самых объектов.

    +

    Заключение

    +

    Оператор instanceof должен использоваться только при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора typeof, любого другого использования необходимо избегать.

    +

    Приведение типов

    JavaScript — слабо типизированный язык, поэтому преобразование типов будет применяться везде, где возможно.

    +
    // Эти равенства — истинны
    +new Number(10) == 10; // объект типа Number преобразуется
    +                      // в числовой примитив в результате неявного вызова
    +                      // метода Number.prototype.valueOf
    +
    +10 == '10';           // Strings преобразуется в Number
    +10 == '+10 ';         // Ещё чуток строко-безумия
    +10 == '010';          // и ещё
    +isNaN(null) == false; // null преобразуется в 0,
    +                      // который конечно же не NaN
    +
    +// Эти равенства — ложь
    +10 == 010;
    +10 == '-10';
    + +

    Для того, чтобы избежать этого, настоятельно рекомендуется использовать оператор строгого равенства. Впрочем, хотя это и позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript.

    +

    Конструкторы встроенных типов

    +

    Конструкторы встроенных типов, например, Number и String ведут себя различным образом, в зависимости от того, вызываются они с ключевым словом new или без.

    +
    new Number(10) === 10;     // False, Object и Number
    +Number(10) === 10;         // True, Number и Number
    +new Number(10) + 0 === 10; // True, из-за неявного преобразования
    +

    Использование встроенного типа, такого как Number, в качестве конструктора создаёт новый экземпляр объекта Number, но при использовании без ключевого слова new функция Number будет вести себя как конвертер.

    +

    Кроме того, присутствие литералов или переменных, которые не являются объектами, приведет к еще большему насилию над типами.

    +

    Лучший вариант — это явное приведение к одному из трех возможных типов.

    +

    Приведение к строке

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

    Путём добавления в начале пустой строки, значение легко приводится к строке.

    +

    Приведение к числовому типу

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

    Используя унарный оператор плюс, можно преобразовать значение в число.

    +

    Приведение к булеву типу

    +

    Используя оператор not (!) дважды, значение может быть приведено к логическому (булеву) типу.

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

    Нативности

    Почему нельзя использовать eval

    Функция eval выполняет строку кода JavaScript в локальной области видимости.

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

    Но eval исполняется в локальной области видимости только тогда, когда он вызывается напрямую и при этом имя вызываемой функции именно eval.

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

    Любой ценой избегайте использования функции eval. 99.9% случаев её "использования" могут достигаться без её участия.

    +

    eval под прикрытием

    +

    Обе функции работы с интервалами времени setTimeout и setInterval могут принимать строку в качестве первого аргумента. Эта строка всегда будет выполняться в глобальной области видимости, поскольку eval в этом случае вызывается не напрямую.

    +

    Проблемы с безопасностью

    +

    Кроме всего прочего, функция eval — это проблема в безопасности, поскольку исполняется любой переданный в неё код; никогда не следует использовать её со строками из неизвестных или недоверенных источников.

    +

    Заключение

    +

    Никогда не стоит использовать eval: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходима eval, эта часть должна тут же ставиться под сомнение и не должна использоваться в первую очередь — необходимо найти лучший способ, которому не требуются вызовы eval.

    +

    undefined и null

    В JavaScript есть два отдельных типа для представления ничего, при этом более полезным из них является undefined.

    +

    Тип undefined

    +

    undefined — это тип с единственным возможным значением: undefined.

    +

    Кроме этого, в языке определена глобальная переменная со значением undefined, и эта переменная так и называется — undefined. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить.

    + +

    Несколько случаев, когда возвращается undefined:

    +
      +
    • При попытке доступа к глобальной переменной undefined (если она не изменена).
    • +
    • Неявный возврат из функции при отсутствии в ней оператора return.
    • +
    • Из операторов return, которые ничего не возвращают.
    • +
    • В результате поиска несуществующего свойства у объекта (и доступа к нему).
    • +
    • Параметры, которые не были переданы в функцию явно.
    • +
    • При доступе ко всему, чьим значением является undefined.
    • +
    +

    Обработка изменений значения undefined

    +

    Поскольку глобальная переменная undefined содержит копию настоящего значения undefined, присвоение этой переменной нового значения не изменяет значения типа undefined.

    +

    Но при этом, чтобы сравнить что-либо со значением undefined, прежде нужно получить значение самой переменной undefined.

    +

    Чтобы защитить код от переопределения переменной undefined, часто используется техника анонимной обёртки, которая использует отсутствующий аргумент.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // в локальной области видимости `undefined`
    +    // снова ссылается на правильное значене.
    +
    +})('Hello World', 42);
    +

    Другой способ достичь того же эффекта — использовать определение внутри обёртки.

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

    Единственная разница между этими вариантами в том, что последняя версия будет больше на 4 байта при минификации, а в первом случае внутри анонимной обёртки нет дополнительного оператора var.

    +

    Использование null

    +

    Хотя undefined в контексте языка JavaScript чаще используется в качестве традиционного null, настоящий null (и тип и литерал) является в большей или меньшей степени просто другим типом данных.

    +

    Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов за счёт присваивания Foo.prototype = null). Но в большинстве случаев тип null может быть заменён на undefined.

    +

    Автоматическая вставка точек с запятой

    Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом не принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить.

    +

    При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript автоматически вставляет их в те места, где сталкивается с ошибкой парсинга из-за их отсутствия.

    +
    var foo = function() {
    +} // ошибка разбора, ожидается точка с запятой
    +test()
    +

    Происходит вставка и парсер пытается снова.

    +
    var foo = function() {
    +}; // ошибки нет, парсер продолжает
    +test()
    +

    Автоматическая вставка точек с запятой считается одним из наибольших упущений в проекте языка, поскольку она может изменить поведение кода.

    +

    Как это работает

    +

    Приведённый код не содержит точек с запятой, так что места для их вставки остаются на совести парсера:

    +
    (function(window, undefined) {
    +    function test(options) {
    +        log('тестируем!')
    +
    +        (options.list || []).forEach(function(i) {
    +
    +        })
    +
    +        options.value.test(
    +            'здесь передадим длинную строчку',
    +            'и ещё одну на всякий случай'
    +        )
    +
    +        return
    +        {
    +            foo: function() {}
    +        }
    +    }
    +    window.test = test
    +
    +})(window)
    +
    +(function(window) {
    +    window.someLibrary = {}
    +
    +})(window)
    +

    Ниже представлен результат игры парсера в "угадалки".

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // не вставлена точка с запятой, строки были объединены
    +        log('тестируем!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- вставлена
    +
    +        options.value.test(
    +            'здесь передадим длинную строчку',
    +            'и ещё одну на всякий случай'
    +        ); // <- вставлена
    +
    +        return; // <- вставлена, в результате 
    +                //    оператор return разбит на два блока
    +        { // теперь парсер считает этот блок отдельным
    +
    +            // метка и одинокое выражение
    +            foo: function() {}
    +        }; // <- вставлена
    +    }
    +    window.test = test; // <- вставлена
    +
    +// снова объединились строки
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- вставлена
    +
    +})(window); //<- вставлена
    + +

    Парсер радикально подменил поведение изначального кода, а в определённых случаях он сделал абсолютно неправильные выводы.

    +

    "Висящие" скобки

    +

    Если парсер встречает "висящую" скобку, то он не вставляет точку с запятой.

    +
    log('тестируем!')
    +(options.list || []).forEach(function(i) {})
    +

    Такой код трансформируется в строку

    +
    log('тестируем!')(options.list || []).forEach(function(i) {})
    +

    Чрезвычайно высоки шансы, что log возвращает не функцию; таким образом, эта строка вызовет TypeError с сообщением о том, что undefined не является функцией.

    +

    Заключение

    +

    Настоятельно рекомендуем никогда не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием if / else. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую.

    +

    Другое

    setTimeout и setInterval

    Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции setTimeout и setInterval.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // возвращает число > 0
    +

    Функция setTimeout возвращает идентификатор таймаута и планирует вызвать foo через, примерно, тысячу миллисекунд. Функция foo при этом будет вызвана ровно один раз.

    +

    В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет никакой гарантии, что переданный код будет выполнен ровно через указанное в вызове setTimeout время.

    +

    Переданная первым параметром функция будет вызвана как глобальный объект — это значит, что оператор this в вызываемой функции будет ссылаться на этот самый объект.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this ссылается на глобальный объект
    +        console.log(this.value); // выведет в лог undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    Поочерёдные вызовы с использованием setInterval

    +

    setTimeout вызывает функцию единожды; setInterval — как и предполагает название — вызывает функцию каждые X миллисекунд. И его использование не рекомендуется.

    +

    В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, setInterval будет продолжать планировать последующие вызовы переданной функции. Это может (особенно в случае небольших интервалов) повлечь за собой выстраивание вызовов функций в очередь.

    +
    function foo(){
    +    // что-то, что выполняется одну секунду
    +}
    +setInterval(foo, 100);
    +

    В приведённом коде foo выполнится один раз и заблокирует этим главный поток на одну секунду.

    +

    Пока foo блокирует код, setInterval продолжает планировать последующие её вызовы. Теперь, когда первая foo закончила выполнение, в очереди будут уже десять ожидающих выполнения вызовов foo.

    +

    Разбираемся с потенциальной блокировкой кода

    +

    Самый простой и контролируемый способ — использовать setTimeout внутри самой функции.

    +
    function foo(){
    +    // что-то, выполняющееся одну секунду
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Такой способ не только инкапсулирует вызов setTimeout, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция foo теперь принимает решение, хочет ли она запускаться ещё раз или нет.

    +

    Очистка таймаутов вручную

    +

    Удаление таймаутов и интервалов работает через передачу соответствующего идентификатора либо в функцию clearTimeout, либо в функцию clearInterval — в зависимости от того, какая функция set... использовалась для его получения.

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

    Очистка всех таймаутов

    +

    Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс.

    +
    // удаляем "все" таймауты
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, за счёт чего их можно будет удалять индивидуально.

    +

    Скрытое использование eval

    +

    setTimeout и setInterval могут принимать строку в качестве первого параметра. Эту возможность не следует использовать никогда, поскольку изнутри при этом производится скрытый вызов eval.

    + +
    function foo() {
    +    // будет вызвана
    +}
    +
    +function bar() {
    +    function foo() {
    +        // никогда не будет вызывана
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Поскольку eval в этом случае не вызывается напрямую, переданная в setTimeout строка будет выполнена в глобальной области видимости; так что локальная переменная foo из области видимости bar не будет выполнена.

    +

    По этим же причинам рекомендуется не использовать строку для передачи аргументов в функцию, которая должна быть вызвана из одной из двух функций, работающих с таймаутами.

    +
    function foo(a, b, c) {}
    +
    +// НИКОГДА не делайте такого
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// Вместо этого используйте анонимную функцию
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    Заключение

    +

    Никогда не используйте строки как параметры setTimeout или setInterval. Это явный признак действительно плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать анонимную функцию, которая самостоятельно будет отвечать за сам вызов.

    +

    Кроме того, избегайте использования setInterval в случаях, когда его планировщик может блокировать выполнение JavaScript.

    +

    Пояснения

    От переводчиков

    Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript, рекомендуем прислушиваться к этим советам, но при этом не делать из этого крайность. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально.

    +

    К примеру, в статье про сравнение объектов авторы настоятельно рекомендуют использовать только оператор строгого неравенства ===. Но мы считаем, что если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ =. Вы вольны применять строгое неравенство только в случаях, когда вы не уверены в типах операндов (!== undefined — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности.

    +

    Функцию setInterval тоже можно использовать, если вы стопроцентно уверены, что код внутри неё будет исполняться как минимум в три раза быстрее переданного ей интервала.

    +

    С другой стороны, использование var и грамотная расстановка точек с запятой — обязательные вещи, халатное отношение к которым никак не может быть оправдано — в осознанном пропуске var (если только вы не переопределяете глобальный объект браузера... хотя зачем?) или точки с запятой нет никакого смысла.

    +

    Относитесь с мудростью к тому, что вы пишете — важно знать, как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. Важно знать, как работает прототипное наследование, но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант.

    +

    Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах, или у вас один из тех супер-крупных проектов, которым никогда не помешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/style/garden.css b/external/JavaScript-Garden/style/garden.css new file mode 100644 index 0000000..b6d2930 --- /dev/null +++ b/external/JavaScript-Garden/style/garden.css @@ -0,0 +1,630 @@ +/* Eric Meyer's reset.css v1.0 | 20080212 */ + +html, body, div, span, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, cite, code, +del, em, img, ins, kbd, +strong, sub, sup, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +article, aside, figure, footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + width: 960px; + margin: 0 auto; + font-family: Georgia, 'Nimbus Roman No9 L', 'Liberation Serif', serif; + font-size: 14px; + background: #0D2E4C; + color: #E6EAED; +} + +#intro h1 { + padding: 40px 0 30px; + margin: 0; + font-size: 75px; + line-height: 1.2em; + font-weight: normal; + text-shadow: 0 2px 2px #000; + font-family: Georgia, serif; +} + +/* Navigation */ + +nav { + position: fixed; + top: 0; + right: 0; + padding-bottom: 20px; + height: 100%; + text-shadow: none; + width: 225px; +} + +nav > div li { + float: left; + margin-right: 1px; +} + +nav > div li a, #top, #hide_menu { + padding: 8px 6px 7px; + cursor: pointer; + font-size: 12px; + font-weight: bold; +} + +nav > div li.active a { + border-top: 4px solid #0D2E4C; + padding-top: 4px; + color: #FFC76D; +} + +#hide_menu { + display: none; +} + +#top, #hide_menu { + float: right; +} + +#nav_main > div { + overflow: hidden; + border-bottom: 1px solid #6E8294; +} + +#nav_main > div ul { + float: left; + width: 200px; +} + +#nav_main > ul { + height: calc(100% - 4em); + overflow-y: auto; +} + +nav li { + font-size: 16px; + margin: 0px; + list-style: none; +} + +nav > ul li:last-child a { + padding-bottom: 9px; +} + +nav > ul > li { + border-bottom: 1px solid #6E8294; +} + +/* Sprite map for sidebar icons */ +nav li h1 a { + font-size: 15px; + font-weight: bold; + padding: 7px 20px 10px 32px; + background-image: url('../image/sidebar-icon.png'); + background-repeat: no-repeat; + background-position: 3px 3px; +} + +nav li.nav_intro h1 a { background-image: none; padding-left: 20px; } +nav li.nav_object h1 a { background-position: 5px 3px; } +nav li.nav_function h1 a { background-position: 5px -60px; } +nav li.nav_array h1 a { background-position: 5px -28px; } +nav li.nav_types h1 a { background-position: 5px -92px; } +nav li.nav_core h1 a { background-position: 5px -124px; } +nav li.nav_other h1 a { background-position: 5px -156px; } +nav li.nav_appendix h1 a { background-position: 5px -188px; } + +nav a { + color: white; + font-family: Arial, sans-serif; + font-size: 13px; + display: block; + padding: 4px 20px 6px; +} + +/* Background color for nav elements to animate */ + +#nav_main, +nav a, +nav ul, +nav li { + background-color: #556c81; +} + +nav a:hover, +nav li.active, +nav li.active a { + background-color: #344f68; +} + +nav li.active a:hover, +nav li a.active, +nav li a.active:hover { + background-color: #0D2E4C; +} + +nav a:hover, nav ul.active a:hover, nav a, nav li { + -moz-transition-property: background-color, color; + -moz-transition-duration: 0.3s; + + -webkit-transition-property: background-color, color; + -webkit-transition-duration: 0.3s; + + -o-transition-property: background-color, color; + -o-transition-duration: 0.3s; + + transition-property: background-color, color; + transition-duration: 0.3s; +} + +#nav_mobile { + display: none; +} + +section { + border-bottom: 4px dotted #8696a5; + font-size: 16px; + margin-right: 410px; + position: relative; +} + +section article, +section header { + padding-bottom: 20px; + border-bottom: 1px solid #8696a5; +} + +#intro article, #intro header { + padding-bottom: 0px; + border-bottom: 0; +} + +section article:last-child, +section header:last-child { + border-bottom: 0; +} + +article ul, p { + line-height: 1.6em; + margin-bottom: 1.25em; +} + +pre, code { + font-family: Consolas, Inconsolata, 'Bitstream Vera Sans Mono', Menlo, Monaco, 'Andale Mono', 'Courier New', monospace; +} + +pre { + padding: 8px 0 12px 20px; + border-left: 4px solid #566D82; + margin-bottom: 1.25em; + background: #0F192A; +} + +code { + font-size: 0.875em; +} + +section pre code { + font-size: 0.8125em; + line-height: 1.4em; + padding: 0px; + margin-top: 3px; + margin-bottom: 1px; + background: #0F192A; +} + +section code { + background: #314D67; + padding: 3px 4px 1px; +} + +h2, h3 { + font-family: Arial, sans-serif; + text-shadow: 0 1px 1px #000; +} + +section h1 { + font-size: 2.6em; + padding: 1.2em 0 0.6em; + font-weight: normal; + text-shadow: 0 1px 1px #000; +} + +section header + section h1 { + +} + +/* Adding 'icon' to the side of sections */ + +section:after { + position: absolute; + top: 30px; + right: -190px; + font-size: 80px; + color: #566D82; + width: 180px; + text-align: center; +} + +/* Not used for desktop version */ +nav li h1 a:after { + display: none; +} + +nav li.nav_intro h1 a:after { content: 'i'; font-style: italic; } +#object:after, nav li.nav_object h1 a:after { content: '{ }'; } +#array:after, nav li.nav_array h1 a:after { content: '[ ]'; } +#types:after, nav li.nav_types h1 a:after { content: '='; font-weight: bold; } +#core:after, nav li.nav_core h1 a:after { content: 'js'; } + +#appendix:after, nav li.nav_appendix h1 a:after { + content: '*'; + font-size: 130px; +} + +#function:after, nav li.nav_function h1 a:after { + content: 'f(x)'; + font-style: italic; +} + +#other:after, nav li.nav_other h1 a:after { + content: '?'; + font-size: 100px; + font-family: Arial, Helvetica, sans-serif; + font-weight: bold; +} + +/* Headers */ + +h2 { + font-size: 1.5em; + margin: 0 0 0.7em; + padding-top: 1.5em; +} + +h3 { + font-size: 1.125em; + margin: 1.33em 0 0.88em; +} + +article div { + position: relative; +} + +/* Asides */ + +aside { + border-bottom: 1px solid #9eabb7; + padding-bottom: 0.625em; + position: absolute; + width: 180px; + right: -190px; + opacity: 0.7; + + -moz-transition: opacity 0.3s; + -webkit-transition: opacity 0.3s; + -o-transition: opacity 0.3s; +} + +aside:last-child { + bottom: 0; +} + +aside:hover { + opacity: 1; +} + +aside p { + margin: 0; + font-size: 0.8125em; + font-family: Arial; + line-height: 1.3em; +} + +aside p + p { + margin-top: 1.5em; +} + +aside.es5:after { + content: 'ES5'; + font-family: Georgia, serif; + position: absolute; + font-size: 32px; + font-style: italic; + top: -18px; + right: -3px; + + -ms-filter: “progid:DXImageTransform.Microsoft.Alpha(Opacity=25)”; /* Just kill me already... */ + opacity: 0.25; +} + +/* Article elements */ + +section ol { + margin-bottom: 2em; + list-style: decimal; +} + +section ul { + list-style: square; +} + +section li { + font-size: 0.9375em; + margin-top: 6px; + line-height: 1.3em; +} + +footer { + padding-top: 10px; +} + +a { + color: #FFA500; + text-decoration: none; +} + +a:hover, a.active { + color: #FFC76D; +} + +abbr { + cursor: help; + border-bottom: 1px dotted; +} + +a abbr { + cursor: pointer; +} + +/* Syntax Highlighting */ + +.str { color: #1DC116; } +.kwd { color: #E83737; } +.com { color: #428BDD; } +.typ { color: #FFAA3E; } +.lit { color: #D1EDFF; } +.pun { color: #D1EDFF; } +.tag { color: #008; } +.atn { color: #606; } +.atv { color: #080; } +.dec { color: #606; } +.pln { color: #FFAA3E; } + +/* Desktop override */ + +@media not device and screen and (min-width: 1000px) { + #nav_main { + display: block !important; + top: 0 !important; + } +} + +/* "Tablet" Styles */ + +@media screen and (max-width: 999px) { + body { + width: 100%; + max-width: 999px; + overflow-x: hidden; + } + + section, footer { + font-size: inherit; + margin-left: 30px; + margin-right: 0; + } + + #nav_main > div ul { + width: auto; + } + + nav > div li a, #top, #hide_menu { + font-size: 14px; + padding: 12px 12px 10px; + } + + nav > div li.active a { + padding-top: 8px; + } + + #nav_main { + display: none; + height: auto; + margin: 0; + width: 100%; + z-index: 100; + padding: 0; + + -moz-box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3); + } + + #nav_main ul ul { + display: none !important; + } + + #nav_main > ul > li { + float: left; + text-align: center; + width: 14.28%; + border-bottom: 0; + } + + #nav_main li h1 a { + background-image: none; + height: 5em; + padding: 1.5em 0 2em; + } + + #nav_main li h1 a:after { + display: block; + font-family: Georgia; + font-size: 3em; + font-weight: normal; + margin-top: 10px; + } + + #nav_main > div ul { + height: auto; + } + + nav a:hover, + nav li.active, + nav li.active a { + background-color: #0D2E4C; + } + + #hide_menu { + display: block; + } + + #nav_mobile { + position: fixed; + width: 24%; + right: 0; + height: 0; + display: block; + z-index: 50; + } + + #nav_mobile a { + display: block; + background-color: #556C81; + padding: 8px 15px; + margin-bottom: 1px; + text-align: right; + } + + #nav_next_section, #nav_prev_section { + font-size: 0.55em; + text-transform: uppercase; + } + + #nav_next_section .nav_section_name, + #nav_prev_section .nav_section_name { + display: block; + font-size: 1.5em; + margin-top: 3px; + font-weight: bold; + line-height: 1.3em; + text-transform: none; + } + + #show_menu { + font-weight: bold; + cursor: pointer; + } + + pre { + border-left: 0; + padding-left: 12px; + overflow: auto; + } + + section { + width: 70%; + } + + aside, section:after { + right: -33%; + width: 30%; + } + + section:after { + display: none; + } +} + +/* "Smartphone" styles */ + +@media screen and (max-width: 600px) { + body { + max-width: 600px; + } + + section { + margin-right: 30px; + width: auto; + } + + section:after { + right: 0; + font-size: 60px; + } + + aside { + position: static; + width: auto; + margin: 1em; + padding: 0.6em 0.8em 0.625em; + border-top: 1px solid #9eabb7; + opacity: 0.85; + } + + #nav_main > ul > li { + float: left; + text-align: center; + border-bottom: 0; + } + + #nav_mobile { + width: 160px; + } + + aside.es5:after { + display: none; + } +} + +/* Small Screen "Smarphone" Vertical Mode */ + +@media screen and (max-width: 360px) { + #nav_main > ul > li { + float: left; + text-align: center; + width: 33.33%; + border-bottom: 0; + } +} diff --git a/external/JavaScript-Garden/style/print.css b/external/JavaScript-Garden/style/print.css new file mode 100644 index 0000000..f14677e --- /dev/null +++ b/external/JavaScript-Garden/style/print.css @@ -0,0 +1,152 @@ +body { + font-family: Arial, sans-serif; + width: 100%; + background: #fff; + color: #000; +} + +section { + margin-right: 0; +} + +nav { + display: none; +} + +#intro h1 { + font-size: 35px; + padding: 20px 0; +} + +header h1 { + font-size: 20px; + padding: 20px 0; +} + +h2 { + margin: 20px 0 10px; + font-size: 16px; + padding-top: 0; +} + +h3 { + font-size: 13px; + margin: 15px 0 8px; +} + +h2, h3 { + font-variant: normal; + text-transform: capitalize; + page-break-after: avoid; +} + +header h1, #intro h1, +h2, h3 { + text-shadow: none; +} + +article h2 > code, article h3 > code { + text-transform: none; + font-size: 0.9em; +} + +article h2 a { + display: none; +} + +article { + font-size: 11px; + width: 78%; + padding-bottom: 0; + border-bottom-width: 2px; +} + +header article { + +} + +aside { + width: 26%; + right: -28%; + padding-bottom: 6px; + opacity: 1; +} + +aside.es5:after { + display: none; +} + +a { + color: inherit; +} + +p, ul, ol { + orphans: 3; + line-height: 1.3em; +} + +pre { + background: #eee; + padding: 6px 0 6px 20px; +} + +aside p { + font-size: 10px; + color: #666; +} + +article code, article pre code { + background: #eee; +} + +article code { + padding: 1px 3px; +} + +article pre code { + padding: 0px; +} + +article li { + margin-top: 6px; +} + +article ul, article ol { + list-style-position: inside; + margin-left: 8px; +} + +.com { + color: #600; + font-style: italic; +} + +.typ { + color: #404; + font-weight: bold; +} + +.lit { + color: #044; +} + +.pun { + color: #440; +} + +.pln { + color: #444; +} + +.atn { + color: #404; +} + +.str, .atv { + color: #060; +} + +.kwd, .tag { + color: #006; + font-weight: bold; +} diff --git a/external/JavaScript-Garden/tr/index.html b/external/JavaScript-Garden/tr/index.html new file mode 100644 index 0000000..84c5f8e --- /dev/null +++ b/external/JavaScript-Garden/tr/index.html @@ -0,0 +1,1507 @@ +JavaScript Garden

    Giriş

    Giriş

    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 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 +gerekir. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, lütfen +Mozilla Programcı Ağı'nda bulunan mükemmel rehbere başvurun.

    +

    Yazarlar

    +

    Bu rehber, sevimli birer Stack Overflow kullanıcısı olan Ivo Wetzel (Yazım) +ve Zhang Yi Jiang (Tasarım) tarafından hazırlanmıştır.

    +

    Katkıda Bulunanlar

    + +

    Sunum

    +

    JavaScript Garden GitHub üzerinden, ve ayrıca Cramer Development +tarafından desteklenen JavaScriptGarden.info adresinden sunulmaktadır.

    +

    Lisans

    +

    JavaScript Garden MIT lisansı altında yayınlanmıştır ve GitHub +üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına +rastlarsanız lütfen sorunu bize bildirin veya bir pull request gönderin. +Bizi ayrıca Stack Overflow'da JavaScript sohbet odasında da +bulabilirsiniz.

    +

    Nesneler

    Nesne Kullanımı ve Özellikleri

    JavaScript'te iki istisna dışında her şey bir nesne olarak davranır; +bu istisnalar da null ve undefined +'dır.

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

    Sık düşülen bir yanılgı sayı sabitlerinin nesne olarak kullanılamayacağıdır. Bu +yanılgının sebebi de JavaScript çözümleyicisinin nokta notasyonu ile girilen +sayıları bir reel sayı olarak algılama hatasıdır.

    +
    2.toString(); // SyntaxError hatası verir
    +

    Bu hatayı aşıp sayı sabitlerinin de nesne olarak davranmasını sağlamak için +uygulanabilecek bazı çözümler vardır.

    +
    2..toString(); // ikinci nokta doğru şekilde algılanır
    +2 .toString(); // noktanın solundaki boşluğa dikkat edin
    +(2).toString(); // ilk önce 2 değerlendirilir
    +

    Bir veri türü olarak nesneler

    +

    JavaScript nesneleri aynı zamanda bir Hashmap olarak da kullanılabilir, +nesneler temelde isimli özellikler ve bunlara karşılık gelen değerlerden +ibarettir.

    +

    Nesne sabiti ({} notasyonu) ile düz bir nesne yaratmak mümkündür. Bu yeni +nesne kalıtım ile Object.prototype 'dan türüyecektir ve +hiçbir baz özelliğe sahip olmayacaktır.

    +
    var foo = {}; // yeni bir boş nesne
    +
    +// adı 'test' ve değeri 12 olan bir özelliği sahip yeni bir nesne
    +var bar = {test: 12}; 
    +

    Özelliklere erişmek

    +

    Bir nesnenin özelliklerine iki yolla erişilebilir, ya nokta notasyonu ile veya +köşeli parantez notasyonu ile.

    +
    var foo = {name: 'kitten'}
    +foo.name; // kitten
    +foo['name']; // kitten
    +
    +var get = 'name';
    +foo[get]; // kitten
    +
    +foo.1234; // SyntaxError
    +foo['1234']; // çalışır
    +

    Her iki notasyon da aynı şekilde çalışır, tek fark köşeli parantez notasyonunun +özelliklerin dinamik olarak oluşturulmasına ve normalde bir yazım hatasına yol +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.

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

    Yukarıdaki örnek sonuç olarak hem bar undefined hem de foo null yazacaktır. +Sadece baz özelliği kaldırılmış olacak ve çıktıda görünmeyecektir.

    +

    Anahtar notasyonu

    +
    var test = {
    +    'case': 'anahtar kelime olduğu için katar olarak girildi',
    +    delete: 'yine bir anahtar kelime' // SyntaxError hatası
    +};
    +

    Nesne özellikleri düz karakterler olarak da katar notasyonu ile de +tanımlanabilir. Fakat JavaScript çözümleyicisinin bir başka tasarım hatası +yüzünden, yukarıdaki örnek ECMAScript 5 öncesinde bir SyntaxError hatası +verecektir.

    +

    Bu hata delete 'in bir anahtar kelime olmasından kaynaklanır, bu nedenle +eski JavaScript motorlarının bu örneği doğru algılaması için karakter katarı +notasyonu ile girilmelidir.

    +

    Prototip

    JavaScript klasik bir kalıtım modeli değil prototip modeli kullanır.

    +

    Çoğu zaman bu modelin JavaScript'in zayıf yönlerinden biri olduğu söylense de, +aslında prototip model klasik modelden daha güçlüdür. Mesela prototip model +temel alınarak klasik kalıtım modeli oluşturulabilir, fakat bunun tersini yapmak +çok daha zordur.

    +

    Prototip kalıtım modeli kullanan tek popüler dil JavaScript olduğu için iki +model arasındaki farklılıklara alışmak biraz zaman alır.

    +

    İlk büyük farklılık JavaScript'te kalıtımın prototip zincirleri ile +yapılmasıdır.

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// Bar nesnesinin prototipi olarak yeni bir Foo nesnesini ata
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// Nesne oluşturucusunun Bar olmasını sağla
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar() // yeni bir Bar oluştur
    +
    +// Sonuçta ortaya çıkan prototip zinciri
    +test [bir Bar sınıfı nesnesi]
    +    Bar.prototype [bir Foo sınıfı nesnesi] 
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* vs. */ }
    +

    Yukarıda, test nesnesi hem Bar.prototype hem de Foo.prototype 'dan +türeyecektir; bu nedenle Foo 'da tanımlanmış olan method fonksiyonuna +da erişebilir. Ayrıca, prototipi olan tek Foo nesnesinin value +özelliğine de erişebilir. Dikkat edilmesi gereken bir nokta, new Bar() +ifadesinin yeni bir Foo nesnesi yaratmayıp, prototipine atanmış olan +nesneyi kullanmasıdır; bu nedenle, tüm Bar nesneleri aynı value +özelliğine sahip olacaktır.

    + +

    Özelliklere bulmak

    +

    Bir nesnenin özelliklerine erişildiğinde, JavaScript, istenen isimdeki özelliği +bulana kadar prototip zincirinde yukarı doğru dolaşır.

    +

    Zincirin en üstüne ulaştığında (yani Object.prototype) ve hala istenen özelliği +bulamamışsa sonuç olarak undefined verecektir.

    +

    prototype özelliği

    +

    prototype özelliği dil tarafından prototip zincirleri oluşturmak için +kullanılsa da, bu özelliğe herhangi bir değer atamak mümkündür. Fakat +prototip olarak atanan ilkel nesne türleri göz ardı edilecektir.

    +
    function Foo() {}
    +Foo.prototype = 1; // hiç bir etkisi olmaz
    +

    Bir önceki örnekte gösterildiği gibi, prototip olarak nesneler atanabilir, bu da +prototip zincirlerinin dinamik olarak oluşturulabilmesini sağlar.

    +

    Performans

    +

    Prototip zincirinin yukarısındaki özellikleri aramanın performansı kritik olan +programlarda olumsuz etkileri olabilir. Ek olarak, mevcut olmayan özelliklere +erişmeye çalışmak da tüm prototip zincirinin baştan sona taranmasına neden +olacaktır.

    +

    Ayrıca, bir nesnenin özellikleri üzerinde iterasyon +yapıldığında da prototip zinciri üzerindeki tüm özelliklere bakılacaktır.

    +

    Temel prototiplerin genişletilmesi

    +

    Sıklıkla yapılan bir hata Object.prototype 'ı veya diğer baz prototipleri +genişletmektir.

    +

    Bu tekniğe monkey patching denir ve kapsüllemeyi bozar. Bu teknik +Prototype gibi bazı popüler sistemlerde kullanılsa bile, temel nesne +türlerine standart olmayan özellikler eklenmesinin geçerli iyi bir nedeni +yoktur.

    +

    Temel prototipleri genişletmenin tek bir geçerli nedeni vardır, o da daha +yeni JavaScript motorlarında bulunan özelliklerin eski motorlara getirilmesidir; +mesela Array.forEach.

    +

    Sonuç

    +

    Prototip kalıtım modeli kullanan karmaşık programlar yazmadan önce bu modelin +tamamen anlaşılması şarttır. Ayrıca, prototip zincirinin uzunluğuna dikkat +edilmeli ve çok uzaması durumunda performans sorunları yaşamamak için parçalara +bölünmelidir. Bundan başka, temel prototipler yeni JavaScript motorları ile +uyumluluk sağlamak dışında bir nedenle asla genişletilmemelidir.

    +

    hasOwnProperty

    Bir özelliğin nesnenin prototip zinciri üzerinde bir yerde +değil, kendisi üzerinde tanımlandığını belirlemek için, Object.prototype +kalıtımı ile tüm nesnelerin sahip olduğu hasOwnProperty metodunun kullanılması +gerekir.

    + +

    hasOwnProperty JavaScript'te nesne özellikleri üzerinde çalışıp prototip +zincirinin tümünü dolaşmayan tek şeydir.

    +
    // Object.prototype'a bar özelliğini ekle
    +Object.prototype.bar = 1; 
    +var foo = {goo: undefined};
    +
    +foo.bar; // 1
    +'bar' in foo; // true
    +
    +foo.hasOwnProperty('bar'); // false
    +foo.hasOwnProperty('goo'); // true
    +

    Sadece hasOwnProperty beklenen doğru sonucu verecektir, nesne özellikleri +üzerinde iterasyon yaparken bu çok önemlidir. Bir nesnenin kendisi üzerinde +değil de protip zinciri üzerinde bir yerde tanımlanmış olan özelliklerini +çıkarmanın başka hiçbir yolu yoktur.

    +

    hasOwnProperty özelliği

    +

    JavaScript hasOwnProperty adının bir özellik olarak kullanılmasını engellemez; +bu nedenle bir nesnenin bu isimde bir özelliğe sahip olması ihtimali varsa, +doğru sonuç alabilmek için hasOwnPropertyharicen kullanılmalıdır.

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // her zaman false verir
    +
    +// hasOwnProperty başka bir nesne üzerinde
    +// kullanıldığında 'this' foo olur
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    Sonuç

    +

    Bir nesnenin bir özelliği sahip olup olmadığını kontrol etmek için +kullanılabilecek tek yöntem hasOwnProperty 'dir. Aynı zamanda, nesne +prototiplerinin genişletilmesinden kaynaklanabilecek +hataların önüne geçmek için, tüm for in döngüleri ile +hasOwnProperty kullanılması tavsiye olunur.

    +

    for in Döngüsü

    Tıpkı in operatörü gibi for in döngüsü de bir nesnenin özellikleri üzerinde +iterasyon yaparken prototip zincirini dolaşır.

    + +
    // Object.prototype'a bar özelliğini ekle
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // hem bar hem de moo yazar
    +}
    +

    for in döngüsünün davranışını değiştirmek mümkün olmadığı için, istenmeyen +özelliklerin döngünün içinde filtrelenmesi gerekir, bu da Object.prototype +nesnesinin hasOwnProperty metodu ile yapılır.

    + +

    hasOwnProperty kullanarak filtrelemek

    +
    // yukarıdaki örnekteki foo nesnesi
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    Doğru kullanım bu yeni versiyonda gösterildiği gibidir. hasOwnProperty kontrol +edildiği için sadece moo yazacaktır. hasOwnProperty kullanılmaz ise ve +Object.prototype 'ın baz özellikleri değiştirilmişse, program bazı hatalara +yatkın olabilir.

    +

    Bunu yapan ve yaygın olarak kullanılan bir JavaScript sistemi Prototype +'dır. Bu sistemde hasOwnProperty kullanmayan for in döngüleri kesinlikle +hatalı sonuç verecektir.

    +

    Sonuç

    +

    hasOwnProperty her zaman kontrol edilmelidir. Programın içinde çalıştığı +ortam için, nesnelerin baz özelliklerinin değiştirilip değiştirilmediğine dair +hiçbir kabul yapılmamalıdır.

    +

    Fonksiyonlar

    Fonksiyon Tanımlaması ve Fonksiyon İfadesi

    Fonksiyonlar JavaScript'te birinci sınıf nesnelerdir, yani sıradan bir değer +gibi kullanılabilirler. Bu özellik sıklıkla bir isimsiz fonksiyonu başka bir +fonksiyona - ki bu muhtemelen asenkron bir fonksiyondur - callback olarak +geçirmekte kullanılır.

    +

    function tanımlaması

    +
    function foo() {}
    +

    Yukarıdaki fonksiyon tanımlaması program çalışmadan önce +yukarı taşınır ve böylece tanımlandığı kapsam içinde +her yerde (hatta tanımlanmadan önce bile) kullanılabilir.

    +
    foo(); // foo bu satır çalışmadan önce oluşturuldu
    +function foo() {}
    +

    function ifadesi

    +
    var foo = function() {};
    +

    Bu örnekte isimsiz fonksiyon foo değişkenine atanır.

    +
    foo; // 'undefined'
    +foo(); // Bu satır bir TypeError hatasına neden olur
    +var foo = function() {};
    +

    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 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 olacaktır.

    +

    İsimli fonksiyon ifadesi

    +

    Bir başka özel durum isimli fonksiyon ifadesidir.

    +
    var foo = function bar() {
    +    bar(); // Çalışır
    +}
    +bar(); // ReferenceError hatası verir
    +

    Burada bar fonksiyonuna dış kapsamdan ulaşılamaz, çünkü sadece foo +değişkenine atanmıştır; fakat iç kapsamda bar fonksiyonuna erişilebilir. +Bunun nedeni JavaScript'te isim çözümlemenin çalışma +şeklidir, fonksiyonun adına fonksiyonun içinden her zaman erişilebilir.

    +

    this Nasıl Çalışır

    JavaScript'te this özel kelimesinin anlamı diğer programlama dillerinden +farklıdır. this kelimesinin birbirinden farklı anlamlar yüklendiği tam +beş durum vardır.

    +

    Genel kapsam

    +
    this;
    +

    this kelimesi genel kapsamda kullanıldığında global nesneye işaret eder.

    +

    Bir fonksiyon çağırma

    +
    foo();
    +

    Burada this yine global nesneye işaret eder.

    + +

    Bir metod çağırma

    +
    test.foo(); 
    +

    Bu örnekte this kelimesi test 'e işaret edecektir.

    +

    Bir nesne oluşturucu çağırma

    +
    new foo(); 
    +

    Bir fonksiyon başında new anahtar kelimesi ile birlikte çağrılırsa bir +nesne oluşturucu olarak davranır. Bu fonksiyonun +içinde this kelimesi yeni oluşturulan Object 'e işaret eder.

    +

    this kelimesinin atanması

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // dizi aşağıdaki gibi açılır
    +foo.call(bar, 1, 2, 3); // sonuç: a = 1, b = 2, c = 3
    +

    Function.prototype 'ın call veya apply metodları kullanıldığında, çağrılan +fonksiyonun içinde this 'in değeri ilk argümanın değeri olarak atanır.

    +

    Sonuç olarak, yukarıdaki örnekte metod çağırma durumu geçerli olmayacak, +bunun yerine foo fonksiyonu içinde this 'in değeri bar olacaktır.

    + +

    Sık düşülen yanılgılar

    +

    Yukarıdaki durumların çoğu mantıklı görünse bile, ilk durum dilin tasarım +hatalarından biri olarak değerlendirilmelidir çünkü hiçbir pratik +kullanılımı yoktur.

    +
    Foo.method = function() {
    +    function test() {
    +        // this genel nesneye işaret eder
    +    }
    +    test();
    +};
    +

    Bir başka yanılgı test fonksiyonunun içinde this 'in Foo 'ya işaret +edeceğinin sanılmasıdır, ama bu doğru değildir.

    +

    test fonksiyonu içinden Foo 'ya erişmenin yolu method içinde bir lokal +değişken oluşturmaktır.

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // Burada this yerine that kullanın
    +    }
    +    test();
    +};
    +

    that kelimesinin dilde özel bir anlamı yoktur, ama sıklıkla dış kapsamdaki +this 'e işaret etmek için kullanılır. Bu yöntem closure +kavramı ile birlikte kullanıldığında this değerini program içinde taşımaya da +yarar.

    +

    Metodları değişkenlere atamak

    +

    JavaScript'te mevcut olmayan bir başka özellik de fonksiyon isimlendirmedir, +başka bir deyişle bir metodu bir değişkene atamak.

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

    İlk durum nedeniyle test artık sıradan bir fonksiyon olarak davranacaktır; bu +nedenle test fonksiyonu içinde this artık someObject 'e işaret +etmeyecektir.

    +

    this kelimesinin geç bağlanması ilk bakışta yanlış görünse de, aslında +prototipsel kalıtımı mümkün kılan şey budur.

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

    Yukarıda Bar sınıfına ait bir nesnenin method 'u çağrıldığında this bu +nesneye işaret edecektir.

    +

    Closure ve Referanslar

    JavaScript'in en güçlü özelliklerinden biri de closure 'lara sahip olmasıdır. +Bunun anlamı her hangi bir kapsamın her zaman kendisini içeren kapsama +erişebilmesidir. JavaScript'te tek kapsam fonksiyon kapsamı +olduğu için temelde tüm fonksiyonlar closure 'durlar.

    +

    Private değişkenler

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

    Burada, Counter iki closure verir: increment fonksiyonu ve get +fonksiyonu. Bu iki fonksiyon da Counter fonksiyonun kapsamına ait bir +referans 'a sahiptir, ve bu nedenle söz konusu kapsamda tanımlanmış olan +count değişkenine erişebilirler.

    +

    Private değişkenler nasıl işler

    +

    JavaScript'te kapsam referanslarına erişmek yada atama yapmak mümkün olmadığı +için, dış kapsamdan count değişkenine ulaşmak mümkün değildir. Bu +değişkene ulaşmanın tek yolu yukarıdaki iki closure 'dur.

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

    Bu program parçası Counter fonksiyonun kapsamındaki count değişkeninin +değerini değiştirmez, çünkü foo.hack bu kapsamda tanımlanmamıştır. +Bunun yerine global kapsamda yeni bir değişen oluşturur (yada mevcut bir +değişkeni değiştirir).

    +

    Döngü içinde closure

    +

    Sık yapılan bir hata, döngü içinde closure kullanıp döngünün indeks değişkeninin +değerinin kopyalanacağını varsaymaktır.

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

    Yukarıdaki örnek çıktı olarak 0 - 9 arası sayıları vermek yerine, 10 +sayısını on kez yazacaktır.

    +

    İçteki isimsiz fonksiyon i değişkeninin değerine değil referansına sahiptir +ve console.log çağrıldığında, for döngüsü çoktan tamamlanmış ve i +değişkeninin değeri 10 olmuştur.

    +

    İstenen davranışı elde etmek için i değişkeninin değerinin kopyalanması +gerekir.

    +

    Referans probleminin çözümü

    +

    Döngünün indeks değişkeninin değerini kopyalamanın en iyi yolu bir +isimsiz fonksiyon kullanmaktır.

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

    Dıştaki isimsiz fonksiyon her adımda çağrılacak ve e parametresi olarak +i 'nin değerinin bir kopyası verilecektir.

    +

    setTimeOut fonksiyonuna verilen isimsiz fonksiyon artık e 'ye ait bir +referansa sahip olacaktır, ve referansın değeri döngü tarafından +değiştirilmeyecektir.

    +

    Bu davranışı başka bir yolla da elde etmek mümkündür; isimsiz fonksiyondan başka +bir fonksiyon döndürmek. Bu durumda yukarıdaki ile aynı davranış elde +edilecektir.

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

    arguments Nesnesi

    JavaScript'te her fonksiyon kapsamında arguments adlı özel bir nesne +tanımlıdır. Bu nesne fonksiyon çağrılırken verilen argümanların listesini +içerir.

    + +

    arguments nesnesi bir Array değildir. Bir dizinin özelliklerinin bir +kısmına sahip olsa da (length özelliği) Array.prototype sınıfından +türetilmemiştir, aslında bir Object bile değildir.

    +

    Bu nedenle, arguments nesnesi üzerinde push, pop ve slice gibi standart +dizi metotlarını kullanmak mümkün değildir. Klasik for döngüsü arguments +nesnesi ile kullanılabilir, ancak standart dizi metotlarını kullanmak için +gerçek bir diziye dönüştürmek gerekir.

    +

    Diziye dönüştürmek

    +

    Aşağıdaki program parçası arguments nesnesinin tüm elemanlarına sahip yeni bir +dizi verecektir.

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

    Bu dönüşüm yavaştır, ve performansın belirleyici olduğu durumlarda +kullanılması tavsiye olunmaz.

    +

    Argümanların geçirilmesi

    +

    Aşağıdaki örnekte, argümanların bir fonksiyondan diğerine geçirilmesi +için önerilen yöntem gösterilmiştir.

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

    Bir başka püf noktası da call ve apply 'ı birlikte kullanarak hızlı, +ilişkisiz fonksiyonlar yaratmaktır.

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// "method" 'un ilişkisiz bir versiyonunu yarat
    +// Aldığı parametreler: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // Sonuç: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    Tanımlı parametreler ve argüman indisleri

    +

    arguments nesnesi her iki özelliği ve fonksiyonun tanımlı parametreleri için +getter ve setter fonksiyonlar oluşturur.

    +

    Sonuç olarak, bir tanımlı parametrenin değerini değiştirmek arguments +nesnesindeki karşılık gelen özelliğin değerini de değiştirecektir.

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

    Performans mitleri ve gerçekler

    +

    arguments nesnesi fonksiyon kapsamında bir değişken veya tanımlı parametre +olarak kullanılmış olması durumları dışında her zaman oluşturulur. Kullanılıp +kullanılmaması fark etmez.

    +

    getter ve setter fonksiyonlar her zaman oluşturulur; dolayısıyla +arguments nesnesini kullanmanın performans üzerinde olumsuz bir etkisi yoktur, +özellikle de sadece arguments nesnesinin özelliklerine erişmekten ibaret +olmayan gerçek programlarda.

    + +

    Fakat, modern JavaScript motorlarının performansını ciddi bir şekilde etkileyen +bir durum vardır. Bu durum arguments.callee nesnesinin kullanılmasıdır.

    +
    function foo() {
    +    arguments.callee; // içinde olduğumuz fonksiyon nesnesi
    +    arguments.callee.caller; // ve çağıran fonksiyon nesnesi
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // Normalde inline edilirdi...
    +    }
    +}
    +

    Yukarıdaki program parçasında, foo fonksiyonuna inlining uygulanması +mümkün değildir çünkü fonksiyonun hem kendisini ve kendisini çağıran fonksiyonu +bilmesi gerekmektedir. Bu yüzden hem inlining yapılamadığı için bir performans +artışı sağlanamamış hem de kapsüllenme bozulmuş olmaktadır, çünkü fonksiyon +artık kendisini çağıran kapsama bağımlı hale gelmiş olabilir.

    +

    arguments.callee ve özelliklerinin asla kullanılmaması +şiddetle tavsiye olunur.

    + +

    Nesne Oluşturucular

    JavaScript'te oluşturucular diğer dillerden farklıdır. Başında new bulunan +her fonksiyon çağrısı bir oluşturucudur.

    +

    Oluşturucunun (çağrılan fonksiyonun) içinde this 'in değeri yeni yaratılan +Object 'dir. Bu yeni nesnenin prototipi oluşturucu +olarak çağrılan fonksiyon nesnesinin prototipidir.

    +

    Çağrılan fonksiyonda bir return ifadesi yoksa, this (yani yeni nesneyi) +döndürür.

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

    Yukarıdaki program Foo oluşturucusunu çağırır ve yeni yaratılan nesnenin +prototipini Foo.prototype olarak belirler.

    +

    Oluşturucunun içinde bir return ifadesi bulunması durumunda, ve sadece +bu değer bir Object ise oluşturucu fonksiyon verilen değeri döndürür.

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // yeni bir Bar nesnesi
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // döndürülen nesne
    +

    new anahtar kelimesi ihmal edilirse, fonksiyon yeni bir nesne döndürmez.

    +
    function Foo() {
    +    this.bla = 1; // global nesnenin özelliğini değiştirir
    +}
    +Foo(); // undefined
    +

    Yukarıdaki örnek bazı durumlarda doğru çalışıyor gibi görünebilir, ama +JavaScript'te this 'in çalışma şeklinden dolayı this +'in değeri global nesne olacaktır.

    +

    Nesne fabrikaları

    +

    new anahtar kelimesini ihmal edebilmek için oluşturucu fonksiyonun bir değer +döndürmesi gerekir.

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

    Yukarıda Bar fonksiyonunu çağıran her iki ifade de aynı şeyi döndürecektir: +method adında bir closure özelliği olan yeni yaratılmış +bir nesne.

    +

    Başka bir nokta da new Bar() fonksiyonunun döndürülen nesnenin prototipini +etkilememesidir. Yeni nesnenin prototipi oluşturulacaktır ancak Bar bu +nesneyi döndürmez.

    +

    Yukarıdaki örnekte new anahtar kelimesini kullanmakla kullanmamak arasında +hiçbir bir fark yoktur.

    +

    Fabrikalar ile yeni nesneler oluşturmak

    +

    new anahtar kelimesinin kullanılmaması tavsiye edilir, çünkü unutulması +durumu hatalara sebep olabilir.

    +

    Bunun yerine yeni bir nesne oluşturmak için bir fabrika kullanılmalıdır.

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

    Yukarıdaki örnek hem new anahtar kelimesinin unutulmasından etkilenmez hem de +private değikenlerin kullanılmasını kolaylaştırır, ama +bazı dezavantajları da vardır.

    +
      +
    1. Oluşturulan nesneler bir prototip üzerinde metotlarını paylaşmadıkları +için daha fazla hafıza kullanılır.
    2. +
    3. Başka bir sınıf türetmek için fabrikanın tüm metotları başka bir nesneden +kopyalaması veya bu nesneyi yeni nesnenin prototipine yerleştirmesi gerekir.
    4. +
    5. Sadece new anahtar kelimesinin ihmal edilmesinden kaynaklanacak sorunları +gidermek için prototip zincirinden vazgeçmek dilin ruhuna aykırıdır.
    6. +
    +

    Sonuç

    +

    new anahtar kelimesini ihmal etmek hatalara neden olabilir, fakat bu +kesinlikle prototip zincirinden vazgeçmek için bir neden olamaz. Hangi +çözümün belirli bir programa uygun olduğu kararını verirken, en önemli nokta +nesne oluşturmak için belirli bir yöntemi seçip bu çözüme bağlı kalmaktır.

    +

    Kapsamlar ve İsim Uzayları

    JavaScript'te birbiri ile eşleşen ayraçlar kullanılmasına karşın blok +kapsamı bulunmaz; bu nedenle, dilde sadece fonksiyon kapsamı mevcuttur.

    +
    function test() { // fonksiyon kapsamı
    +    for(var i = 0; i < 10; i++) { // kapsam değil
    +        // sayaç
    +    }
    +    console.log(i); // 10
    +}
    + +

    JavaScript'te isim uzayları kavramı da bulunmaz, tanımlanan herşey +genel olarak paylaşılmış tek bir isim uzayının içindedir.

    +

    Bir değişkene erişildiğinde, JavaScript değişkenin tanımını bulana dek yukarıya +doğru tüm kapsamlara bakar. Genel kapsama ulaşıldığı halde hala değişkenin +tanımı bulanamamışsa bir ReferenceError hatası oluşur.

    +

    Genel değişkenler felaketi

    +
    // A programı
    +foo = '42';
    +
    +// B programı
    +var foo = '42'
    +

    Yukarıdaki iki program birbirinden farklıdır. A programında genel kapsamda +bir foo değişkeni tanımlanmıştır, B programındaki foo değişkeni ise mevcut +kapsamda tanımlanmıştır.

    +

    Bu iki tanımlamanın birbirinden farklı etkileri olacaktır, var anahtar +kelimesini kullanmamanın önemli sonuçları olabilir.

    +
    // genel kapsam
    +var foo = 42;
    +function test() {
    +    // lokal kapsam
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    test fonksiyonun içinde var anahtar kelimesinin atlanması genel kapsamdaki +foo değişkeninin değerini değiştirecektir. İlk bakışta bu önemsiz gibi görünse +de, binlerce satırlık bir programda var kullanılmaması korkunç ve takibi güç +hatalara neden olacaktır.

    +
    // genel kapsam
    +var items = [/* bir dizi */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // subLoop fonksiyonun kapsamı
    +    for(i = 0; i < 10; i++) { // var kullanılmamış
    +        // do amazing stuff!
    +    }
    +}
    +

    Dışarıdaki döngüden subLoop fonksiyonu bir kez çağrıldıktan sonra çıkılacaktır, +çünkü subLoop i değişkeninin dış kapsamdaki değerini değiştirir. İkinci +for döngüsünde de var kullanılması bu hatayı kolayca engelleyecektir. +Bilinçli olarak dış kapsama erişilmek istenmiyorsa var ifadesi asla +atlanmamalıdır.

    +

    Lokal değişkenler

    +

    JavaScript'te lokal değişkenler sadece fonksiyon +parametreleri ve var ifadesi ile tanımlanan değişkenlerdir.

    +
    // genel kapsam
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // test fonksiyonunun lokal kapsamı
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    test fonksiyonun içinde foo ve i lokal değişkenlerdir, bar değişkenine +değer atanması ise genel kapsamdaki aynı isimdeki değişkenin değerini +değiştirecektir.

    +

    Yukarı taşıma

    +

    JavaScript'te tanımlamalar yukarı taşınır. Yani hem var ifadesi hem de +function bildirimleri içindeki bulundukları kapsamın en üstüne taşınırlar.

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

    Program çalışmadan önce yukarıdaki kod dönüştürülür. JavaScript, var +ifadelerini ve function bildirimlerini içinde bulundukları kapsamın en üstüne +taşır.

    +
    // var ifadeleri buraya taşınır
    +var bar, someValue; // varsayılan değerleri 'undefined' olur
    +
    +// function bildirimi de yukarı taşınır
    +function test(data) {
    +    var goo, i, e; // blok kapsamı olmadığı için buraya taşınır
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // bir TypeError hatası oluşur çünkü bar hala 'undefined'
    +someValue = 42; // değer atamaları etkilenmez
    +bar = function() {};
    +
    +test();
    +

    Blok kapsamının bulunmaması nedeniyle hem var ifadeleri döngülerin dışına +taşınır hem de bazı if ifadeleri anlaşılmaz sonuçlar verebilir.

    +

    Orijinal programda if ifadesi goo isimli genel değişkeni değiştiriyor gibi +görünüyordu, fakat yukarı taşımadan sonra anlaşıldığı gini aslında +lokal değişkeni değiştiriyor.

    +

    Yukarı taşıma dikkate alınmadığında aşağıdaki programın bir ReferenceError +oluşturacağı sanılabilir.

    +
    // SomeImportantThing değişkenine değer atanmış mı, kontrol et
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    Fakat var değişkeni genel kapsamın en üstüne taşınacağı için bu program +çalışacaktır.

    +
    var SomeImportantThing;
    +
    +// SomeImportantThing arada bir yerde atanmış olabilir
    +
    +// Değer atandığından emin ol
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    İsim çözümleme

    +

    JavaScript'te genel kapsam da dahil tüm kapsamlarda this +adında bir özel değişken tanımlanmıştır, bu değişken geçerli nesneyi gösterir.

    +

    Fonksiyon kapsamlarında aynı zamanda arguments adında +bir değişken tanımlanmıştır ve fonksiyonun argümanlarını içerir.

    +

    Örnek olarak bir fonksiyon kapsamında foo değişkenine erişildiğinde JavaScript +isim çözümlemeyi aşağıdaki sıra ile yapacaktır:

    +
      +
    1. Geçerli kapsamda bir var foo ifadesi mevcutsa bu kullanılır.
    2. +
    3. Fonksiyonun parametrelerinden birinin adı foo ise bu kullanılır.
    4. +
    5. Fonksiyonun kendisinin adı foo ise bu kullanılır.
    6. +
    7. Bir dıştaki kapsama geçilir ve yeniden 1 adımına dönülür.
    8. +
    + +

    İsim uzayları

    +

    Tek bir genel isim uzayının bulunmasının yol açtığı yaygın sonuç isim +çakışmasıdır. JavaScript'te bu sorun isimsiz fonksiyonlar ile kolayca +önlenebilir.

    +
    (function() {
    +    // bir "isim uzayı"
    +
    +    window.foo = function() {
    +        // korunmasız bir closure
    +    };
    +
    +})(); // fonksiyonu hemen çalıştır
    +

    İsimsiz fonksiyonlar ifade olarak değerlendirilir; +bu nedenle çağrılabilmeleri için önce değerlendirilmeleri gerekir.

    +
    ( // parantezin içindeki fonksiyonu değerlendir
    +function() {}
    +) // ve fonksiyon nesnesini döndür
    +() // değerlendirmenin sonucu fonksiyon nesnesini çağır
    +

    Bir fonksiyon ifadesini değerlendirip çağırmanın başka yolları da vardır ve +yukarıdaki ile aynı sonucu verirler.

    +
    // İki farklı yöntem
    ++function(){}();
    +(function(){}());
    +

    Sonuç

    +

    Programı kendi isim uzayı ile kapsamak için her zaman isimsiz fonksiyonların +kullanılması tavsiye edilir. Böylece hem isim çakışmalarından korunulmuş olunur, +hem de programlar daha modüler halde yazılmış olur.

    +

    Ayrıca, genel değişkenlerin kullanılması kötü bir uygulamadır. Genel +değişkenlerin herhangi bir şekilde kullanılmış olması programın kötü yazılmış +olduğuna, hatalara eğilimli olduğuna ve sürdürülmesinin zor olacağına işaret +eder.

    +

    Diziler

    Dizi İterasyonu ve Özellikleri

    Diziler JavaScript nesneleri olmalarına rağmen, iterasyon yapmak için +for in döngüsü kullanmak için bir neden yoktur. +Aslında dizilerde for in kullanılmasına karşı bazı iyi nedenler +vardır.

    + +

    for in döngüsü prototip zincirindeki tüm özellikleri dolaştığı için ve bunu +engellemenin tek yolu hasOwnProperty kullanmak +olduğu için for in döngüsü sıradan bir for döngüsünden yirmi kata kadar +daha yavaştır.

    +

    İterasyon

    +

    Dizilerde iterasyon yaparken en iyi performansı elde etmenin en iyi yolu klasik +for döngüsünü kullanmaktır.

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

    Yukarıdaki örnekte bir optimizasyon var, o da dizinin uzunluğun iterasyonun +başında l = list.length ile saklanmış olması.

    +

    length özelliği dizinin kendisinde tariflenmiş olmasına rağmen, her adımda +bu özelliği okumanın yine de bir maliyeti vardır. Modern JavaScript motorları +bu tür durumlar için muhtemelen optimizasyon yapıyor olsa bile, programın +her zaman modern bir motorda çalışacağından emin olmak mümkün değildir.

    +

    Aslında, yukarıdaki örnekteki optimizasyonu uygulamamak döngünün +iki kat daha yavaş çalışmasına neden olabilir.

    +

    length özelliği

    +

    length özelliğine değer atanarak diziyi kısaltmak için kullanılabilir.

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

    Daha küçük bir uzunluk atanması diziyi kısaltır, fakat daha büyük bir uzunluk +atanmasının dizi üzerinde bir etkisi yoktur.

    +

    Sonuç

    +

    En iyi performans için her zaman sıradan for döngüsü kullanılmalı ve +length özelliği saklanmalıdır. Dizilerde for in döngüsünün kullanılmış +olması hatalara meyilli kötü yazılmış bir programa işaret eder.

    +

    Array Oluşturucusu

    Array oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu +için, yeni diziler oluşturulurken her zaman dizi sabitlerinin ([] +notasyonu) kullanılması tavsiye olunur.

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

    Array oluşturucusuna tek bir argüman verildiğinde, ve bu argümanın türü +Number ise, oluşacak boş dizinin length özelliği argümanın +değerine eşit olacaktır. Bu şekilde oluşturulan bir dizinin sadece +length özelliği belirlenmiş olup dizi indisleri tanımsız olacaktır.

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, indisler atanmadı
    +

    Dizinin uzunluğunu bu şekilde önceden belirlemek sadece bir iki durumda +kullanışlıdır. Bunlardan birisi bir döngüye gerek olmadan bir karakter +katarını tekrarlamaktır.

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

    Sonuç

    +

    Array oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır. +Bunun yerine her zaman dizi sabitleri tercih edilmelidir. Hem daha kısadırlar +hem de daha anlaşılır bir sentaksa sahiptirler; bu nedenle programın +okunabilirliğini de artırırlar.

    +

    Nesne Tipleri

    Eşitlik ve Karşılaştırmalar

    JavaScript'de nesnelerin değerlerinin eşitliğini kontrol etmenin iki farklı yolu +vardır.

    +

    Eşitlik operatörü

    +

    Eşitlik operatörü iki adet eşittir işaretinden oluşur: ==

    +

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

    Yukarıdaki tablo tip dönüşümünün sonuçlarını verir, ve == kullanımının kötü +bir uygulama olarak değerlendirilmesinin başlıca sebebidir. Bu karmaşık dönüşüm +kuralları tespit edilmesi zor hatalara neden olur.

    +

    Ayrıca tip dönüşümü işin içine girdiğinde performans üzerinde de olumsuz etkisi +olur; mesela, bir katarın bir sayı ile karşılaştırılabilmesi için önce bir +sayıya dönüştürülmesi gerekir.

    +

    Kesin eşitlik operatörü

    +

    Kesin eşitlik operatörü üç adet eşittir işaretinden oluşur: ===

    +

    Eşitlik operatörünün aksine, keşin eşitlik operatörü karşılaştırdığı değerler +arasında tip dönüşümü yapmaz.

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

    Yukarıdaki sonuçlar hem daha anlaşılırdır, hem de progamdaki hataların erkenden +ortaya çıkmasını sağlar. Bu programı bir miktar sağlamlaştırır ve ayrıca +karşılaştırılan değerlerin farklı tiplerden olması durumunda performansı da +artırır.

    +

    Nesneleri karşılaştırmak

    +

    Hem == hem de === operatörlerinin eşitlik operatörü olarak +adlandırılmasına rağmen, değerlerden en azından birinin bir Object olması +durumunda farklı davranış gösterirler.

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

    Bu durumda her iki operatör de eşitlik değil aynılık karşılaştırması +yapar; yani, terimlerin aynı nesnenin örnekleri olup olmadığını kontrol +ederler, tıpkı Python dilindeki is ve C dilindeki gösterici karşılaştırması +gibi.

    +

    Sonuç

    +

    Sadece kesin eşitlik operatörünün kullanılması şiddetle tavsiye edilir. +Tip dönüşümü yapılmasının gerekli olduğu durumlarda, bu açıkça +yapılmalıdır ve dilin karmaşık dönüşüm kurallarına bırakılmamalıdır.

    +

    typeof Operatörü

    typeof operatörü (instanceof ile birlikte) +herhalde JavaScript'in en büyük tasarım hatalarından biridir, çünkü neredeyse +tamamen arızalıdır.

    +

    instanceof operatörünün sınırlı kullanımı olsa da, typeof operatörünün +gerçekte tek bir pratik kullanımı vardır, ve bunun da bir nesnenin tipini +kontrol etmekle ilgili yoktur.

    + +

    JavaScript tip tablosu

    +
    Değer               Sınıf      Tip
    +-------------------------------------
    +"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
    +

    Yukarıdaki tabloda Tip sütunu typeof operatörünün verdiği sonucu gösterir. +Açıkça görülebileceği gibi, bu sonuç tutarlı olmaktan çok uzaktır.

    +

    Sınıf sütunu bir nesnenin dahili [[Class]] özelliğini gösterir.

    + +

    [[Class]] özelliğinin değerini almak için Object.prototype 'ın toString +metodu kullanılmalıdır.

    +

    Bir nesnenin sınıfı

    +

    Spesifikasyona göre [[Class]] değerine erişmenin tek yolu +Object.prototype.toString kullanmaktır.

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

    Yukarıdaki örnekte, Object.prototype.toString çağrıldığında +this 'in değeri [[Class]] değeri aranan nesne olarak +atanmış olmaktadır.

    + +

    Bir değişkenin tanımlandığını kontrol etmek

    +
    typeof foo !== 'undefined'
    +

    Yukarıdaki satır foo değişkeninin tanımlanıp tanımlanmadığını belirler; +tanımlanmamış bir değişkene erişmek bir ReferenceError hatası oluştur. +typeof operatörünün tek kullanışlı olduğu şey işte budur.

    +

    Sonuç

    +

    Bir nesnenin tipini kontrol etmek için Object.prototype.toString 'in +kullanılması şiddetle tavsiye edilir; çünkü bunu yapmanın tek güvenilir yoludur. +Yukarıdaki tip tablosunda gösterildiği gibi, typeof operatörünün bazı +sonuçları spesifikasyonda tanımlanmamıştır; bu nedenle, çeşitli platformlarda +farklılık gösterebilirler.

    +

    Bir değişkenin tanımlandığını kontrol etmek dışında, typeof operatörün +kullanımından her ne pahasına olursa olsun kaçınılmalıdır.

    +

    instanceof Operatörü

    instanceof operatörü verilen iki terimin nesne oluşturucularını karşılaştırır. +Kullanışlı olduğu tek durum özel nesnelerin karşılaştırılmasıdır. Temel nesneler +üzerinde kullanıldığında neredeyse typeof operatörü kadar +yararsızdır.

    +

    Özel nesneleri karşılaştırmak

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// Bu satır sadece Bar.prototype'a Foo fonksiyon nesnesinin atar
    +// Bir Foo sınıfı nesnesine değil
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    Temel nesnelerle instanceof kullanımı

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

    Dikkat edilmesi gereken ilginç bir nokta, instanceof operatörünün farklı +JavaScript kaynaklarından gelen nesneler üzerinde çalışmamasıdır (mesela bir +internet tarayıcısının farklı dökümanları), çünkü bu durumda nesne +oluşturucuları aynı olmayacaktır.

    +

    Sonuç

    +

    instanceof operatörü sadece aynı JavaScript kaynağından gelen özel +nesneler ile kullanılmalıdır. Tıpkı typeof operatöründe +olduğu gibi, bunun dışındaki tüm kullanımlarından kaçınılmalıdır.

    +

    Tip Dönüşümleri

    JavaScript weakly typed bir dildir, bu yüzden mümkün olan yerlerde +tip dönüşümü uygular.

    +
    // Bunlar true verir
    +new Number(10) == 10; // Number.toString() tekrar sayıya
    +                      // dönüştürülür
    +
    +10 == '10';           // Katarlar sayıya dönüştürülür
    +10 == '+10 ';         // Bir başka katar çılgınlığı
    +10 == '010';          // Ve bir tane daha 
    +isNaN(null) == false; // null 0'a dönüştürülür
    +                      // tabii 0 NaN değildir
    +
    +// Bunlar false verir
    +10 == 010;
    +10 == '-10';
    + +

    Yukarıdakilerden kaçınmak için, kesin eşitlik operatörünün +kullanılması şiddetle tavsiye edilir. Böylece yaygın hataların çoğundan +kaçınılabilir, yine de JavaScript'in weak typing sisteminden kaynaklanan başka +sorunlar da vadır.

    +

    Temel tiplerin nesne oluşturucuları

    +

    Number ve String gibi temel tiplerin nesne oluşturucuları new anahtar +kelimesi ile kullanılıp kullanılmamalarına göre farklı davranış gösterir.

    +
    new Number(10) === 10;     // False, Object ve Number
    +Number(10) === 10;         // True, Number ve Number
    +new Number(10) + 0 === 10; // True, tip dönüşümü nedeniyle
    +

    Number gibi bir temel tipin nesne oluşturucusunu kullanmak yeni bir Number +nesnesi yaratacaktır, fakat new kelimesi kullanılmazsa Number fonksiyonu +bir dönüştürücü olarak davranacaktır.

    +

    Ayrıca, sabitler ve nesne olmayan değerler kullanılması durumunda başka tür +dönüşümler de söz konusu olacaktır.

    +

    En iyi seçenek üç olası tipten birine açıkça dönüşüm yapılmasıdır.

    +

    Karakter katarına dönüştürmek

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

    Bir değerin başına boş bir katar eklenerek kolayca katara dönüştürülebilir.

    +

    Sayıya dönüştürmek

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

    Tek terimli toplama operatörü kullanılarak bir değer sayıya dönüştürülebilir.

    +

    Mantıksal değişken tipine dönüştürmek

    +

    Değil operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken +tipine dönüştürülebilir.

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

    Temel

    Neden eval Kullanılmamalı

    eval fonksiyonu bir JavaScript kodunu lokal kapsamda yürütür.

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

    Fakat eval sadece direkt olarak çağrıldığında ve çağrılan fonksiyonun +adı eval ise lokal kapsamda çalışır.

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

    eval fonksiyonu asla kullanılmamalıdır. Kullanıldığı durumların %99.9'unda +eval kullanılmadan da istenen sonuç elde edilebilir.

    +

    Gizli eval

    +

    Zamanlama fonksiyonları setTimeout ve setInterval'ın her +ikisinin de ilk argümanları bir karakter katarıdır. Bu durumda eval dolaylı +olarak çağrıldığı için bu argüman her zaman genel kapsamda yürütülecektir.

    +

    Güvenlik sorunları

    +

    eval kendisine verilen her kodu işlettiği için aynı zamanda bir güvenlik +sorunudur ve asla kaynağı bilinmeyen yada güvenilir olmayan karakter +katarları ile kullanılmamalıdır.

    +

    Sonuç

    +

    eval asla kullanılmamalıdır, kullanan programlar ise doğruluk, performans ve +güvenlik açılarından sorgulanmalıdır. eval kullanımı gerekli görülmüşse, +programın tasarımı sorgulanmalı ve kullanılmamalı, bunun yerine eval +gerektirmeyen daha iyi bir tasarım kullanılmalıdır.

    +

    undefined ve null

    JavaScript'te tanımsız anlamına gelen iki değer vardır, ve bunlardan +undefined daha kullanışlıdır.

    +

    undefined değeri

    +

    undefined bir değişken türüdür ve tek bir değere sahip olabilir: undefined.

    +

    JavaScript'te ayrıca değeri undefined olan bir de genel kapsam değişkeni +tanımlanmıştır ve bu değişkenin adı da undefined'dır. Fakat bu değişken +bir sabit yada dilin anahtar kelimelerinden biri değildir. Yani bu +değişkenin değeri kolayca değiştirilebilir.

    + +

    undefined değerinin verildiği durumlara bazı örnekler:

    +
      +
    • Genel kapsamlı undefined değişkeninin (değiştirilmedi ise) değeri
    • +
    • return ifadesi içermeyen fonksiyonların verdiği değer
    • +
    • Bir değer döndürmeyen return ifadeleri
    • +
    • Mevcut olmayan nesne özellikleri
    • +
    • Değer atanmamış fonksiyon parametreleri
    • +
    • Değeri undefined olarak atanmış değişkenler
    • +
    +

    undefined değerinin değiştirilmesi durumu

    +

    Genel kapsamdaki undefined değişkeni asıl undefined değerinin kopyasını +tuttuğu için, bu değeri değiştirmek undefined değişken türünün değerini +değiştirmez.

    +

    Fakat, bir şeyi undefined ile karşılaştırmak için önce undefined'ın değerini +geri almak gerekir.

    +

    Programı undefined değişkeninin değiştirilmesi olasılığına karşı korumak için +uygulanan yaygın bir yöntem isimsiz bir fonksiyona +kullanılmayan bir parametre eklemektir.

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // lokal kapsamda undefined değişkeni
    +    // yine undefined değerine sahip
    +
    +})('Hello World', 42);
    +

    Benzer bir yöntem yine isimsiz fonksiyonun içinde değer atanmamış bir değişken +deklare etmektir.

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

    Buradaki tek fark program sıkıştırılırsa ortaya çıkacaktır, eğer fonksiyonun +başka bir yerinde var ifadesi kullanılmıyorsa fazladan 4 bayt kullanılmış +olacaktır.

    +

    null kullanımı

    +

    JavaScript dilinde undefined geleneksel null yerine kullanılmaktadır, asıl +null (hem null değişmezi hem de değişken türü) ise kabaca başka bir +veri türüdür.

    +

    null JavaScript içinde kapalı olarak kullanılır (mesela prototip zincirinin +sonuna gelindiği Foo.prototype = null ile belirtilir), fakat hemen her durumda +bunun yerine undefined kullanılabilir.

    +

    Otomatik Noktalı Virgül İlavesi

    JavaScript sentaksı C'ye benzese de, noktalı virgül kullanılması +zorunlu değildir.

    +

    Fakat JavaScript noktalı virgül kullanmayan bir dil değildir, hatta +programı anlayabilmek için noktalı virgüllere ihtiyaç duyar. Bu yüzden +JavaScript gramer çözümleyicisi eksik bir noktalı virgül yüzünden bir +hata ile karşılaştığında otomatik olarak eksik noktalı virgülleri +ekler.

    +
    var foo = function() {
    +} // hata, noktalı virgül gerekiyor
    +test()
    +

    Eklemeden sonra çözümleme tekrarlanır.

    +
    var foo = function() {
    +}; // hata ortadan kalktı, çözümleme devam edebilir
    +test()
    +

    Noktalı virgüllerin bu şekilde otomatik olarak eklenmesi JavaScript'in +en büyük tasarım hatalarından biri olarak kabul edilir, çünkü programın +davranışını değiştirmesi mümkündür.

    +

    Ekleme nasıl olur

    +

    Aşağıdaki örnekte hiç noktalı virgül yok, bu yüzden nereye noktalı virgül +eklenmesi gerektiğini gramer çözümleyicinin karar vermesi gerekiyor.

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

    Çözümleyicinin "tahmin" oyununun sonucu aşağıdadır.

    +
    (function(window, undefined) {
    +    function test(options) {
    +
    +        // Eklenmedi, satırlar birleştirildi
    +        log('testing!')(options.list || []).forEach(function(i) {
    +
    +        }); // <- eklendi
    +
    +        options.value.test(
    +            'long string to pass here',
    +            'and another long string to pass'
    +        ); // <- eklendi
    +
    +        return; // <- eklendi, return ifadesi bozuldu
    +        { // bir blok olarak değerlendirildi
    +
    +            // bir yer etiketi ve bir ifade
    +            foo: function() {} 
    +        }; // <- eklendi
    +    }
    +    window.test = test; // <- eklendi
    +
    +// Burada da satırlar birleştirildi
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- eklendi
    +
    +})(window); //<- eklendi
    + +

    Çözümleyici yukarıdaki program parçasının davranışını büyük ölçüde değiştirdi, +belirli durumlarda da grameri değerlendirirken yanlış kararlar verdi.

    +

    Satır başındaki parantezler

    +

    Bir satırın parantez ile başlaması durumunda, çözümleyici noktalı virgül +eklemez.

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

    Yukarıdaki program parçası aşağıdaki tek satıra dönüşür.

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

    Büyük ihtimalle yukarıdaki log bir fonksiyon döndürmüyordur; +bu nedenle, yukarıdaki satır undefined is not a function hata mesajı ile bir +TypeError oluştumasına neden olacaktır.

    +

    Sonuç

    +

    Noktalı virgüllerin hiç bir zaman ihmal edilmemesi tavsiye edilir, ayrıca +ayraçların kendilerinden önceki ifade ile aynı satırda tutulması ve tek satırlık +if ve else ifadelerinde bile ayraçların ihmal edilmemesi önerilir. Her iki +önlem de hem programın tutarlılığını artıracak, hem de JavaScript +çözümleyicisinin programın davranışını değiştirmesini engelleyecektir.

    +

    delete Operatörü

    Kısacası, genel kapsamda tanımlanmış değişkenleri, fonksiyonları ve DontDelete +niteliğine sahip bazı başka şeyleri silmek imkansızdır.

    +

    Genel kapsam ve fonksiyon kapsamı

    +

    Bir değişken veya fonksiyon genel kapsamda veya +fonksiyon kapsamında tanımlandığında aktivasyon nesnesinin +veya global nesnenin bir özelliği olacaktır. Bu tür özelliklerin bir takım +nitelikleri vardır ve bunlardan biri DontDelete niteliğidir. Genel kapsamda ve +fonksiyon kapsamında tanımlanan değişkenler ve fonksiyonlar yaratıldıklarında +her zaman DontDelete niteliğine sahip olacaktır, ve bu nedenle silinemezler.

    +
    // genel kapsam değişkeni:
    +var a = 1; // DontDelete niteliğine sahip
    +delete a; // false
    +a; // 1
    +
    +// normal bir fonksiyon:
    +function f() {} // DontDelete niteliğine sahip
    +delete f; // false
    +typeof f; // "function"
    +
    +// başka bir değişkene atamak işe yaramaz:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    Açıkça tanımlanan özellikler

    +

    Açıkça tanımlanan özellikleri silmek mümkündür.

    +
    // tanımlanan özellik:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    Yukarıdaki örnekte obj.x ve obj.y silinebilir çünkü DontDelete niteliğine +sahip değillerdir. Aynı nedenle aşağıdakini yapmak da mümkündür:

    +
    // IE hariç çalışır:
    +var GLOBAL_OBJECT = this;
    +GLOBAL_OBJECT.a = 1;
    +a === GLOBAL_OBJECT.a; // true - genel değişken
    +delete GLOBAL_OBJECT.a; // true
    +GLOBAL_OBJECT.a; // undefined
    +

    Burada a'yı silmek için bir hile kullanıyoruz. this +burada genel nesneye işaret ediyor ve a değişkenini onun özelliği olarak +atıyoruz, ve böylece onu silebiliyoruz.

    +

    IE (en azından 6-8) bazı hatalar içerdiğinden yukarıdaki örnek çalışmayacaktır.

    +

    Fonksiyon argümanları ve önceden tanımlı özellikler

    +

    Fonksiyonlara verilen argümanlar, arguments nesnesi +ve önceden tanımlı özellikler de DontDelete niteliğine sahiptir.

    +
    // fonksiyon argümanları ve özellikler:
    +(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 nesneler

    +

    Host nesneler üzerinde kullanıldığında delete operatörünün davranışı belirsiz +olabilir. Standarda göre host nesneler istedikleri davranışı uygulayabilirler.

    +

    Sonuç

    +

    delete operatörünün davranışı genellikle belirsizdir ve güvenle kullanılabileceği +tek yer sıradanan nesneler üzerinde açıkça tanımlanan özelliklerdir.

    +

    Diğer

    setTimeout ve setInterval

    JavaScript asenkron olduğu için setTimeout ve setInterval kullanarak bir +fonksiyonun ileri bir zamanda çalışmasını sağlamak mümkündür.

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // 0'dan büyük bir sayı verir
    +

    Yukarıdaki örnekte setTimeout fonksiyonu çağrıldığında, oluşturulan +zamanlayıcı tanımlayan bir ID sayısı verir ve foo fonksiyonu yaklaşık +bin milisaniye sonra çalıştırılmak üzere programlanır. foo fonksiyonu +tam olarak bir kez çağrılacaktır.

    +

    Kullanılan JavaScript motorunun zamanlayıcı hassasiyetine bağlı olarak, ve +ayrıca JavaScript tek thread ile çalıştığı ve çalışan başka program +parçaları bu tek thread 'i bloke edeceği için, setTimeout ile belirlenen +erteleme süresinin tam olarak gerçekleşeceği hiçbir şekilde garanti +edilemez.

    +

    İlk argüman olarak verilen fonksiyon global nesne tarafından çağrılacaktır, +yani çağrılan fonksiyonun içinde this bu nesneye işaret +edecektir.

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this global nesneye işaret eder
    +        console.log(this.value); // undefined yazar
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    setInterval ile fonksiyon çağrılarının yığılması

    +

    setTimeout verilen fonksiyonu bir kez çağırırken, setInterval (adından da +anlaşılacağı gibi) verilen fonksiyonu her X milisaniyede bir çağırır. +Fakat kullanılması önerilmez.

    +

    Mevcut program parçası çalışırken zamanlama bloke olduğu halde, setInterval +verilen fonksiyonu çağırmaya devam edecektir. Bu da, özellikle küçük aralıklarla +kullanıldığında, fonksiyon çağrılarının istiflenmesine neden olur.

    +
    function foo(){
    +    // 1 saniye süren bir işlem
    +}
    +setInterval(foo, 100);
    +

    Yukarıdaki örnekte foo fonksiyonu bir kez çağrılıp bir saniye boyunca bloke +edecektir.

    +

    foo programı bloke etmişken, setInterval fonksiyon çağrılarını zamanlamaya +devam edecektir. foo tamamlandığında, çalıştırılmayı bekleyen on çağrı +daha olacaktır.

    +

    Bloke eden programlarla başa çıkmak

    +

    En kolay ve kontrol edilebilir çözüm, setTimeout 'u fonksiyonun içinde +kullanmaktır.

    +
    function foo(){
    +    // 1 saniye süren bir işlem
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    Bu örnekte hem setTimeout çağrısı fonksiyonun kendisi içinde kapsanmış olmakta, +hem de fonksiyon çağrılarının istiflenmesinin önüne geçilerek daha fazla kontrol +sağlanmaktadır. Artık foo fonksiyonunun kendisi tekrar çalışmak isteyip +istemediğine karar verebilir.

    +

    Zamanlayıcıları iptal etmek

    +

    Zamanlayıcıları iptal etmek için ilgili ID sayıları ile kullanılan zamanlayıcı +fonksiyonuna karşılık gelen clearTimeout ve clearInterval fonksiyonlarından +biri kullanılır.

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

    Tüm zamanlayıcıları iptal etmek

    +

    Tüm zamanlayıcıları iptal etmenin dahili bir yolu olmadığı için, bu amaca +ancak kaba kuvvetle ulaşılabilir.

    +
    // "tüm" zamanlayıcıları iptal et
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    Bu rastgele seçilmiş sayıdan etkilenmeyen zamanlayıcılar kalabilir; bu yüzden +tüm zamanlayıcı ID'lerinin saklanarak, teker teker iptal edilmeleri tavsiye +edilir.

    +

    eval fonksiyonun gizli kullanımı

    +

    setTimeout ve setInterval fonksiyonları ilk parametreleri olarak bir katar +da kabul eder. Bu özellik asla kullanılmamalıdır, çünkü bu durumda dahili +olarak eval kullanılır.

    + +
    function foo() {
    +    // setTimeOut ile bu fonksiyon çağrılacaktır
    +}
    +
    +function bar() {
    +    function foo() {
    +        // bu fonksiyon çağrılmayacaktır
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    Bu durumda eval direkt olarak çağrılmadığı için, setTimeout +fonksiyonuna verilen katar genel kapsamda çalıştırılacaktır; bu nedenle, +bar fonksiyonu kapsamındaki lokal foo değişkenini kullanmayacaktır.

    +

    Zamanlama fonksiyonlarına verilen fonksiyona argüman sağlamak için de bir katar +kullanılması tavsiye edilmez.

    +
    function foo(a, b, c) {}
    +
    +// ASLA bu şekilde kullanılmamalı
    +setTimeout('foo(1, 2, 3)', 1000)
    +
    +// Bunu yerine isimsiz bir fonksiyon kullanın
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    Sonuç

    +

    setTimeout veya setInterval fonksiyonlarına asla bir katar parametre +verilmemelidir. Bu kullanım çok kötü bir programa işaret eder. Çağrılan +fonksiyona argümanlar verilmesinin gerektiği durumlarda gerçek çağrıyı içinde +bulunduran bir isimsiz fonksiyon kullanılmalıdır.

    +

    Ayrıca, setInterval fonksiyonu çalışan JavaScript programı tarafından bloke +olmadığı için tercih edilmemelidir.

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/zh/index.html b/external/JavaScript-Garden/zh/index.html new file mode 100644 index 0000000..fae4268 --- /dev/null +++ b/external/JavaScript-Garden/zh/index.html @@ -0,0 +1,1264 @@ +JavaScript 秘密花园

    简介

    对象

    对象使用和属性

    JavaScript 中所有变量都可以当作对象使用,除了两个例外 nullundefined

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

    一个常见的误解是数字的字面值(literal)不能当作对象使用。这是因为 JavaScript 解析器的一个错误, +它试图将点操作符解析为浮点数字面值的一部分。

    +
    2.toString(); // 出错:SyntaxError
    +

    有很多变通方法可以让数字的字面值看起来像对象。

    +
    2..toString(); // 第二个点号可以正常解析
    +2 .toString(); // 注意点号前面的空格
    +(2).toString(); // 2先被计算
    +

    对象作为数据类型

    +

    JavaScript 的对象可以作为哈希表使用,主要用来保存命名的键与值的对应关系。

    +

    使用对象的字面语法 - {} - 可以创建一个简单对象。这个新创建的对象从 Object.prototype +继承下来,没有任何自定义属性

    +
    var foo = {}; // 一个空对象
    +
    +// 一个新对象,拥有一个值为12的自定义属性'test'
    +var bar = {test: 12}; 
    +

    访问属性

    +

    有两种方式来访问对象的属性,点操作符和中括号操作符。

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

    两种语法是等价的,但是中括号操作符在下面两种情况下依然有效

    +
      +
    • 动态设置属性
    • +
    • 属性名不是一个有效的变量名(译者注比如属性名中包含空格,或者属性名是 JS 的关键词)
    • +
    + +

    删除属性

    +

    删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, +而仅仅是移除了属性和值的关联。

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

    上面的输出结果有 bar undefinedfoo null - 只有 baz 被真正的删除了,所以从输出结果中消失。

    +

    属性名的语法

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

    对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计, +上面的第二种声明方式在 ECMAScript 5 之前会抛出 SyntaxError 的错误。

    +

    这个错误的原因是 delete 是 JavaScript 语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下也能正常运行, +必须使用字符串字面值声明方式。

    +

    原型

    JavaScript 不包含传统的类继承模型,而是使用 prototype 原型模型。

    +

    虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 +例如,很容易通过原型继承实现传统的类继承模型,但是反过来,通过传统的类继承模型来实现原型继承模型就困难得多。 +(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.)

    +

    由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的。

    +

    第一个不同之处在于 JavaScript 使用原型链的继承方式。

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// 设置 Bar 的 prototype 属性为 Foo 的实例对象
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// 修正 Bar.prototype.constructor 为 Bar 本身
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // 创建Bar的一个新实例
    +
    +// 原型链
    +test [Bar的实例]
    +    Bar.prototype [Foo的实例] 
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            {method: ...};
    +            Object.prototype
    +                {toString: ... /* etc. */};
    +

    上面的例子中,test 对象从 Bar.prototypeFoo.prototype 继承下来;因此, +它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。 +需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是 +重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同value 属性。

    + +

    属性查找

    +

    当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。

    +

    到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined

    +

    原型属性

    +

    当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。 +然而将原子类型(primitives)赋给 prototype 的操作将会被忽略。

    +
    function Foo() {}
    +Foo.prototype = 1; // 无效
    +

    而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。

    +

    性能

    +

    如果一个属性在原型链的上端,则对于查找时间将带来不利影响。注意,试图获取一个不存在的属性将会遍历整个原型链。

    +

    并且,当使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问。

    +

    扩展内置类型的原型

    +

    扩展 Object.prototype 或者其他内置类型的原型对象,作为一个错误特性,经常被使用。

    +

    这种技术被称之为 monkey patching 并且会破坏封装。虽然它被广泛的应用到一些 JavaScript 类库中比如 Prototype, +但是我仍然不赞同为内置类型添加一些非标准的函数。

    +

    扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如 Array.forEach

    + +

    总结

    +

    在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 +要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 +更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。

    +

    hasOwnProperty 函数

    为了判断一个对象是否包含自定义属性而不是原型链上的属性, +我们需要使用继承自 Object.prototypehasOwnProperty 方法。

    + +

    hasOwnProperty 是 JavaScript 中唯一一个处理属性但是查找原型链的函数。

    +
    // 修改 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
    +

    只有 hasOwnProperty 可以给出正确和期望的结果。可以查看 for in 循环 +章节来获取关于在迭代遍历对象属性的时候,何时使用 hasOwnProperty 的更多信息。

    +

    hasOwnProperty 作为属性

    +

    JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, +就需要使用外部hasOwnProperty 函数来获取正确的结果。

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // 总是返回 false
    +
    +// 使用其它对象的 hasOwnProperty,并将其上下文设置为 foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +

    结论

    +

    当检查对象上某个属性是否存在时,hasOwnProperty唯一可用的方法。 +同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法, +这将会避免原型对象扩展带来的干扰。

    +

    for in 循环

    in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。

    + +
    // 修改 Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // 输出两个属性:bar 和 moo
    +}
    +

    由于不可能改变 for in 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, +这可以通过 Object.prototype 原型上的 hasOwnProperty 函数来完成。

    + +

    使用 hasOwnProperty 过滤

    +
    // foo 变量是上例中的
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次输出 moo。 +如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。

    +

    一个广泛使用的类库 Prototype 就扩展了原生的 JavaScript 对象。 +因此,当这个类库被包含在页面中时,不使用 hasOwnProperty 过滤的 for in 循环难免会出问题。

    +

    总结

    +

    推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

    +

    函数

    函数声明与表达式

    函数是 JavaScript 中的一等对象,这意味着可以把函数像其它值一样传递。 +一个常见的用法是把匿名函数作为回调函数传递到异步函数中。

    +

    函数声明

    +
    function foo() {}
    +

    上面的方法会在执行前被 解析(hoisted),因此它存在于当前上下文的任意一个地方, +即使在函数定义体的上面被调用也是对的。

    +
    foo(); // 正常运行,因为 foo 在代码运行前已经被创建
    +function foo() {}
    +

    函数赋值表达式

    +
    var foo = function() {};
    +

    这个例子把一个匿名的函数赋值给变量 foo

    +
    foo; // 'undefined'
    +foo(); // 出错:TypeError
    +var foo = function() {};
    +

    由于 var 定义了一个声明语句,对变量 foo 的解析是在代码运行之前,因此 foo 变量在代码运行时已经被定义过了。

    +

    但是由于赋值语句只在运行时执行,因此在相应代码执行之前, foo 的值缺省为 undefined

    +

    命名函数的赋值表达式

    +

    另外一个特殊的情况是将命名函数赋值给一个变量。

    +
    var foo = function bar() {
    +    bar(); // 正常运行
    +}
    +bar(); // 出错:ReferenceError
    +

    bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; +然而在 bar 内部依然可见。这是由于 JavaScript 的 命名处理 所致, +函数名在函数内总是可见的。

    + +

    this 的工作原理

    JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 +在种不同的情况下 ,this 指向的各不相同。

    +

    全局范围内

    +
    this;
    +

    当在全部范围内使用 this,它将会指向全局对象。

    + +

    函数调用

    +
    foo();
    +

    这里 this 也会指向全局对象。

    + +

    方法调用

    +
    test.foo();
    +

    这个例子中,this 指向 test 对象。

    +

    调用构造函数

    +
    new foo();
    +

    如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 +在函数内部,this 指向新创建的对象。

    +

    显式的设置 this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // 数组将会被扩展,如下所示
    +foo.call(bar, 1, 2, 3); // 传递到 foo 的参数是:a = 1, b = 2, c = 3
    +

    当使用 Function.prototype 上的 call 或者 apply 方法时,函数内的 this 将会被 +显式设置为函数调用的第一个参数。

    +

    因此函数调用的规则在上例中已经不适用了,在foo 函数内 this 被设置成了 bar

    + +

    常见误解

    +

    尽管大部分的情况都说的过去,不过第一个规则(译者注这里指的应该是第二个规则,也就是直接调用函数时,this 指向全局对象) +被认为是 JavaScript 语言另一个错误设计的地方,因为它从来就没有实际的用途。

    +
    Foo.method = function() {
    +    function test() {
    +        // this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
    +    }
    +    test();
    +};
    +

    一个常见的误解是 test 中的 this 将会指向 Foo 对象,实际上不是这样子的。

    +

    为了在 test 中获取对 Foo 对象的引用,我们需要在 method 函数内部创建一个局部变量指向 Foo 对象。

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // 使用 that 来指向 Foo 对象
    +    }
    +    test();
    +};
    +

    that 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 this 对象。 +在 闭包 一节,我们可以看到 that 可以作为参数传递。

    +

    方法的赋值表达式

    +

    另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。

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

    上例中,test 就像一个普通的函数被调用;因此,函数内的 this 将不再被指向到 someObject 对象。

    +

    虽然 this 的晚绑定特性似乎并不友好,但这确实是基于原型继承赖以生存的土壤。

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

    method 被调用时,this 将会指向 Bar 的实例对象。

    +

    闭包和引用

    闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 +因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

    + +

    模拟私有变量

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

    这里,Counter 函数返回两个闭包,函数 increment 和函数 get。 这两个函数都维持着 +对外部作用域 Counter 的引用,因此总可以访问此作用域内定义的变量 count

    +

    为什么不可以在外部访问私有变量

    +

    因为 JavaScript 中不可以对作用域进行引用或赋值,因此没有办法在外部访问 count 变量。 +唯一的途径就是通过那两个闭包。

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

    上面的代码不会改变定义在 Counter 作用域中的 count 变量的值,因为 foo.hack 没有 +定义在那个作用域内。它将会创建或者覆盖全局变量 count

    +

    循环中的闭包

    +

    一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号

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

    上面的代码不会输出数字 09,而是会输出数字 10 十次。

    +

    console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时 for循环已经结束, i 的值被修改成了 10

    +

    为了得到想要的结果,需要在每次循环中创建变量 i拷贝

    +

    避免引用错误

    +

    为了正确的获得循环序号,最好使用 匿名包装器译者注其实就是我们通常说的自执行匿名函数)。

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

    外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。

    +

    当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。

    +

    有另一个方法完成同样的工作,那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。

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

    arguments 对象

    JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

    + +

    arguments 变量不是一个数组(Array)。 +尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个对象(Object)。

    +

    因此,无法对 arguments 变量使用标准的数组方法,比如 pushpop 或者 slice。 +虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

    +

    转化为数组

    +

    下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

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

    这个转化比较,在性能不好的代码中不推荐这种做法。

    +

    传递参数

    +

    下面是将参数从一个函数传递到另一个函数的推荐做法。

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // 干活
    +}
    +

    另一个技巧是同时使用 callapply,创建一个快速的解绑定包装器。

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// 创建一个解绑定的 "method"
    +// 输入参数为: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // 结果: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    译者注:上面的 Foo.method 函数和下面代码的效果是一样的:

    +
    Foo.method = function() {
    +    var args = Array.prototype.slice.call(arguments);
    +    Foo.prototype.method.apply(args[0], args.slice(1));
    +};
    +

    自动更新

    +

    arguments 对象为其内部属性以及函数形式参数创建 gettersetter 方法。

    +

    因此,改变形参的值会影响到 arguments 对象的值,反之亦然。

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

    性能真相

    +

    不管它是否有被使用,arguments 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。

    +

    argumentsgetterssetters 方法总会被创建;因此使用 arguments 对性能不会有什么影响。 +除非是需要对 arguments 对象的属性进行多次访问。

    + +

    译者注MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解,请看下面代码:

    +
    // 阐述在 ES5 的严格模式下 `arguments` 的特性
    +function f(a) {
    +  "use strict";
    +  a = 42;
    +  return [a, arguments[0]];
    +}
    +var pair = f(17);
    +console.assert(pair[0] === 42);
    +console.assert(pair[1] === 17);
    +

    然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 arguments.callee

    +
    function foo() {
    +    arguments.callee; // 使用这个函数对象
    +    arguments.callee.caller; // 以及这个函数对象的调用者
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // 通常情况会作为内联函数...
    +    }
    +}
    +

    上面代码中,foo 不再是一个单纯的内联函数 inlining译者注:这里指的是解析器可以做内联处理), +因为它需要知道它自己和它的调用者。 +这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。

    +

    因此强烈建议大家不要使用 arguments.callee 和它的属性。

    + +

    构造函数

    JavaScript 中的构造函数和其它语言中的构造函数是不同的。 +通过 new 关键字方式调用的函数都被认为是构造函数。

    +

    在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 +这个新创建的对象的 prototype 被指向到构造函数的 prototype

    +

    如果被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 - 也就是新创建的对象。

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

    上面代码把 Foo 作为构造函数调用,并设置新创建对象的 prototypeFoo.prototype

    +

    显式的 return 表达式将会影响返回结果,但仅限于返回的是一个对象。

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // 返回新创建的对象
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // 返回的对象
    +

    译者注new Bar() 返回的是新创建的对象,而不是数字的字面值 2。 +因此 new Bar().constructor === Bar,但是如果返回的是数字对象,结果就不同了,如下所示

    +
    function Bar() {
    +    return new Number(2);
    +}
    +new Bar().constructor === Number
    +

    译者注这里得到的 new Test()是函数返回的对象,而不是通过new关键字新创建的对象,因此:

    +
    (new Test()).value === undefined
    +(new Test()).foo === 1
    +

    如果 new 被遗漏了,则函数不会返回新创建的对象。

    +
    function Foo() {
    +    this.bla = 1; // 获取设置全局参数
    +}
    +Foo(); // undefined
    +

    虽然上例在有些情况下也能正常运行,但是由于 JavaScript 中 this 的工作原理, +这里的 this 指向全局对象

    +

    工厂模式

    +

    为了不使用 new 关键字,构造函数必须显式的返回一个值。

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

    上面两种对 Bar 函数的调用返回的值完全相同,一个新创建的拥有 method 属性的对象被返回, +其实这里创建了一个闭包

    +

    还需要注意, new Bar()不会改变返回对象的原型(译者注也就是返回对象的原型不会指向 Bar.prototype)。 +因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回(译者注:而是返回了一个包含 method 属性的自定义对象)。

    +

    在上面的例子中,使用或者不使用 new 关键字没有功能性的区别。

    +

    译者注上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示:

    +
    var bar1 = new Bar();
    +typeof(bar1.method); // "function"
    +typeof(bar1.foo); // "undefined"
    +
    +var bar2 = Bar();
    +typeof(bar2.method); // "function"
    +typeof(bar2.foo); // "undefined"
    +

    通过工厂模式创建新对象

    +

    我们常听到的一条忠告是不要使用 new 关键字来调用函数,因为如果忘记使用它就会导致错误。

    +

    为了创建新对象,我们可以创建一个工厂方法,并且在方法内构造一个新对象。

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

    虽然上面的方式比起 new 的调用方式不容易出错,并且可以充分利用私有变量带来的便利, +但是随之而来的是一些不好的地方。

    +
      +
    1. 会占用更多的内存,因为新创建的对象不能共享原型上的方法。
    2. +
    3. 为了实现继承,工厂方法需要从另外一个对象拷贝所有属性,或者把一个对象作为新创建对象的原型。
    4. +
    5. 放弃原型链仅仅是因为防止遗漏 new 带来的问题,这似乎和语言本身的思想相违背。
    6. +
    +

    总结

    +

    虽然遗漏 new 关键字可能会导致问题,但这并不是放弃使用原型链的借口。 +最终使用哪种方式取决于应用程序的需求,选择一种代码书写风格并坚持下去才是最重要的。

    +

    作用域与命名空间

    尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; +而仅仅支持 函数作用域

    +
    function test() { // 一个作用域
    +    for(var i = 0; i < 10; i++) { // 不是一个作用域
    +        // count
    +    }
    +    console.log(i); // 10
    +}
    + +

    译者注如果 return 对象的左括号和 return 不在一行上就会出错。

    +
    // 译者注:下面输出 undefined
    +function add(a, b) {
    +    return 
    +        a + b;
    +}
    +console.log(add(1, 2));
    +

    JavaScript 中没有显式的命名空间定义,这就意味着所有对象都定义在一个全局共享的命名空间下面。

    +

    每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 +如果到达全局作用域但是这个变量仍未找到,则会抛出 ReferenceError 异常。

    +

    隐式的全局变量

    +
    // 脚本 A
    +foo = '42';
    +
    +// 脚本 B
    +var foo = '42'
    +

    上面两段脚本效果不同。脚本 A 在全局作用域内定义了变量 foo,而脚本 B 在当前作用域内定义变量 foo

    +

    再次强调,上面的效果完全不同,不使用 var 声明变量将会导致隐式的全局变量产生。

    +
    // 全局作用域
    +var foo = 42;
    +function test() {
    +    // 局部作用域
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    在函数 test 内不使用 var 关键字声明 foo 变量将会覆盖外部的同名变量。 +起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 var 声明变量将会带来难以跟踪的 BUG。

    +
    // 全局作用域
    +var items = [/* 数组 */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // subLoop 函数作用域
    +    for(i = 0; i < 10; i++) { // 没有使用 var 声明变量
    +        // 干活
    +    }
    +}
    +

    外部循环在第一次调用 subLoop 之后就会终止,因为 subLoop 覆盖了全局变量 i。 +在第二个 for 循环中使用 var 声明变量可以避免这种错误。 +声明变量时绝对不要遗漏 var 关键字,除非这就是期望的影响外部作用域的行为。

    +

    局部变量

    +

    JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    +
    // 全局变量
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // 函数 test 内的局部作用域
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    fooi 是函数 test 内的局部变量,而对 bar 的赋值将会覆盖全局作用域内的同名变量。

    +

    变量声明提升(Hoisting)

    +

    JavaScript 会提升变量声明。这意味着 var 表达式和 function 声明都将会被提升到当前作用域的顶部。

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

    上面代码在运行之前将会被转化。JavaScript 将会把 var 表达式和 function 声明提升到当前作用域的顶部。

    +
    // var 表达式被移动到这里
    +var bar, someValue; // 缺省值是 'undefined'
    +
    +// 函数声明也会提升
    +function test(data) {
    +    var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // 出错:TypeError,因为 bar 依然是 'undefined'
    +someValue = 42; // 赋值语句不会被提升规则(hoisting)影响
    +bar = function() {};
    +
    +test();
    +

    没有块级作用域不仅导致 var 表达式被从循环内移到外部,而且使一些 if 表达式更难看懂。

    +

    在原来代码中,if 表达式看起来修改了全局变量 goo,实际上在提升规则被应用后,却是在修改局部变量

    +

    如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 ReferenceError

    +
    // 检查 SomeImportantThing 是否已经被初始化
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    实际上,上面的代码正常运行,因为 var 表达式会被提升到全局作用域的顶部。

    +
    var SomeImportantThing;
    +
    +// 其它一些代码,可能会初始化 SomeImportantThing,也可能不会
    +
    +// 检查是否已经被初始化
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    译者注在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

    +
    // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
    +var myvar = 'my value';  
    +
    +(function() {  
    +    alert(myvar); // undefined  
    +    var myvar = 'local value';  
    +})();  
    +

    名称解析顺序

    +

    JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。

    +

    函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。

    +

    比如,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

    +
      +
    1. 当前作用域内是否有 var foo 的定义。
    2. +
    3. 函数形式参数是否有使用 foo 名称的。
    4. +
    5. 函数自身是否叫做 foo
    6. +
    7. 回溯到上一级作用域,然后从 #1 重新开始。
    8. +
    + +

    命名空间

    +

    只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

    +
    (function() {
    +    // 函数创建一个命名空间
    +
    +    window.foo = function() {
    +        // 对外公开的函数,创建了闭包
    +    };
    +
    +})(); // 立即执行此匿名函数
    +

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行。

    +
    ( // 小括号内的函数首先被执行
    +function() {}
    +) // 并且返回函数对象
    +() // 调用上面的执行结果,也就是函数对象
    +

    有一些其他的调用函数表达式的方法,比如下面的两种方式语法不同,但是效果一模一样。

    +
    // 另外两种方式
    ++function(){}();
    +(function(){}());
    +

    结论

    +

    推荐使用匿名包装器译者注也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, +而且有利于程序的模块化。

    +

    另外,使用全局变量被认为是不好的习惯。这样的代码容易产生错误并且维护成本较高。

    +

    数组

    数组遍历与属性

    虽然在 JavaScript 中数组是对象,但是没有好的理由去使用 for in 循环 遍历数组。 +相反,有一些好的理由不去使用 for in 遍历数组。

    + +

    由于 for in 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 hasOwnProperty 函数, +因此会比普通的 for 循环慢上好多倍。

    +

    遍历

    +

    为了达到遍历数组的最佳性能,推荐使用经典的 for 循环。

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

    上面代码有一个处理,就是通过 l = list.length 来缓存数组的长度。

    +

    虽然 length 是数组的一个属性,但是在每次循环中访问它还是有性能开销。 +可能最新的 JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。

    +

    实际上,不使用缓存数组长度的方式比缓存版本要慢很多。

    +

    length 属性

    +

    length 属性的 getter 方式会简单的返回数组的长度,而 setter 方式会截断数组。

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

    译者注: +在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined, undefined, undefined] +但是这个结果并不准确,如果你在 Chrome 的控制台查看 foo 的结果,你会发现是这样的: [1, 2, 3] +因为在 JavaScript 中 undefined 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。

    +
    // 译者注:为了验证,我们来执行下面代码,看序号 5 是否存在于 foo 中。
    +5 in foo; // 不管在 Firebug 或者 Chrome 都返回 false
    +foo[5] = undefined;
    +5 in foo; // 不管在 Firebug 或者 Chrome 都返回 true
    +

    length 设置一个更小的值会截断数组,但是增大 length 属性值不会对数组产生影响。

    +

    结论

    +

    为了更好的性能,推荐使用普通的 for 循环并缓存数组的 length 属性。 +使用 for in 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。

    +

    Array 构造函数

    由于 Array 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - [] - 来创建数组。

    +
    [1, 2, 3]; // 结果: [1, 2, 3]
    +new Array(1, 2, 3); // 结果: [1, 2, 3]
    +
    +[3]; // 结果: [3]
    +new Array(3); // 结果: [] 
    +new Array('3') // 结果: ['3']
    +
    +// 译者注:因此下面的代码将会使人很迷惑
    +new Array(3, 4, 5); // 结果: [3, 4, 5] 
    +new Array(3) // 结果: [],此数组长度为 3
    + +

    由于只有一个参数传递到构造函数中(译者注:指的是 new Array(3); 这种调用方式),并且这个参数是数字,构造函数会返回一个 length 属性被设置为此参数的空数组。 +需要特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成。

    + +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, 数组还没有生成
    +

    这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 for 循环的麻烦。

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

    结论

    +

    应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。

    +

    类型

    相等与比较

    JavaScript 有两种方式判断两个值是否相等。

    +

    等于操作符

    +

    等于操作符由两个等号组成:==

    +

    JavaScript 是弱类型语言,这就意味着,等于操作符会为了比较两个值而进行强制类型转换

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

    上面的表格展示了强制类型转换,这也是使用 == 被广泛认为是不好编程习惯的主要原因, +由于它的复杂转换规则,会导致难以跟踪的问题。

    +

    此外,强制类型转换也会带来性能消耗,比如一个字符串为了和一个数字进行比较,必须事先被强制转换为数字。

    +

    严格等于操作符

    +

    严格等于操作符由个等号组成:===

    +

    不像普通的等于操作符,严格等于操作符不会进行强制类型转换。

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

    上面的结果更加清晰并有利于代码的分析。如果两个操作数类型不同就肯定不相等也有助于性能的提升。

    +

    比较对象

    +

    虽然 ===== 操作符都是等于操作符,但是当其中有一个操作数为对象时,行为就不同了。

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

    这里等于操作符比较的不是值是否相等,而是是否属于同一个身份;也就是说,只有对象的同一个实例才被认为是相等的。 +这有点像 Python 中的 is 和 C 中的指针比较。

    + +

    结论

    +

    强烈推荐使用严格等于操作符。如果类型需要转换,应该在比较之前显式的转换, +而不是使用语言本身复杂的强制转换规则。

    +

    typeof 操作符

    typeof 操作符(和 instanceof 一起)或许是 JavaScript 中最大的设计缺陷, +因为几乎不可能从它们那里得到想要的结果。

    +

    尽管 instanceof 还有一些极少数的应用场景,typeof 只有一个实际的应用(译者注这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值), +而这个应用却不是用来检查对象的类型。

    + +

    JavaScript 类型表格

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

    上面表格中,Type 一列表示 typeof 操作符的运算结果。可以看到,这个值在大多数情况下都返回 "object"。

    +

    Class 一列表示对象的内部属性 [[Class]] 的值。

    + +

    为了获取对象的 [[Class]],我们需要使用定义在 Object.prototype 上的方法 toString

    +

    对象的类定义

    +

    JavaScript 标准文档只给出了一种获取 [[Class]] 值的方法,那就是使用 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
    +

    上面例子中,Object.prototype.toString 方法被调用,this 被设置为了需要获取 [[Class]] 值的对象。

    +

    译者注Object.prototype.toString 返回一种标准格式字符串,所以上例可以通过 slice 截取指定位置的字符串,如下所示:

    +
    Object.prototype.toString.call([])    // "[object Array]"
    +Object.prototype.toString.call({})    // "[object Object]"
    +Object.prototype.toString.call(2)    // "[object Number]"
    + +

    译者注这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示:

    +
    // IE8
    +Object.prototype.toString.call(null)    // "[object Object]"
    +Object.prototype.toString.call(undefined)    // "[object Object]"
    +
    +// Firefox 4
    +Object.prototype.toString.call(null)    // "[object Null]"
    +Object.prototype.toString.call(undefined)    // "[object Undefined]"
    +

    测试未定义变量

    +
    typeof foo !== 'undefined'
    +

    上面代码会检测 foo 是否已经定义;如果没有定义而直接使用会导致 ReferenceError 的异常。 +这是 typeof 唯一有用的地方。

    +

    结论

    +

    为了检测一个对象的类型,强烈推荐使用 Object.prototype.toString 方法; +因为这是唯一一个可依赖的方式。正如上面表格所示,typeof 的一些返回值在标准文档中并未定义, +因此不同的引擎实现可能不同。

    +

    除非为了检测一个变量是否已经定义,我们应尽量避免使用 typeof 操作符。

    +

    instanceof 操作符

    instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 +如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

    +

    比较自定义对象

    +
    function Foo() {}
    +function Bar() {}
    +Bar.prototype = new Foo();
    +
    +new Bar() instanceof Bar; // true
    +new Bar() instanceof Foo; // true
    +
    +// 如果仅仅设置 Bar.prototype 为函数 Foo 本身,而不是 Foo 构造函数的一个实例
    +Bar.prototype = Foo;
    +new Bar() instanceof Foo; // false
    +

    instanceof 比较内置类型

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

    有一点需要注意,instanceof 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, +因为它们的构造函数不会是同一个对象。

    +

    结论

    +

    instanceof 操作符应该仅仅用来比较来自同一个 JavaScript 上下文的自定义对象。 +正如 typeof 操作符一样,任何其它的用法都应该是避免的。

    +

    类型转换

    JavaScript 是弱类型语言,所以会在任何可能的情况下应用强制类型转换

    +
    // 下面的比较结果是:true
    +new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字
    +
    +10 == '10';           // 字符串被转换为数字
    +10 == '+10 ';         // 同上
    +10 == '010';          // 同上 
    +isNaN(null) == false; // null 被转换为数字 0
    +                      // 0 当然不是一个 NaN(译者注:否定之否定)
    +
    +// 下面的比较结果是:false
    +10 == 010;
    +10 == '-10';
    + +

    为了避免上面复杂的强制类型转换,强烈推荐使用严格的等于操作符。 +虽然这可以避免大部分的问题,但 JavaScript 的弱类型系统仍然会导致一些其它问题。

    +

    内置类型的构造函数

    +

    内置类型(比如 NumberString)的构造函数在被调用时,使用或者不使用 new 的结果完全不同。

    +
    new Number(10) === 10;     // False, 对象与数字的比较
    +Number(10) === 10;         // True, 数字与数字的比较
    +new Number(10) + 0 === 10; // True, 由于隐式的类型转换
    +

    使用内置类型 Number 作为构造函数将会创建一个新的 Number 对象, +而在不使用 new 关键字的 Number 函数更像是一个数字转换器。

    +

    另外,在比较中引入对象的字面值将会导致更加复杂的强制类型转换。

    +

    最好的选择是把要比较的值显式的转换为三种可能的类型之一。

    +

    转换为字符串

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

    将一个值加上空字符串可以轻松转换为字符串类型。

    +

    转换为数字

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

    使用一元的加号操作符,可以把字符串转换为数字。

    +

    译者注字符串转换为数字的常用方法:

    +
    +'010' === 10
    +Number('010') === 10
    +parseInt('010', 10) === 10  // 用来转换为整数
    +
    ++'010.2' === 10.2
    +Number('010.2') === 10.2
    +parseInt('010.2', 10) === 10
    +

    转换为布尔型

    +

    通过使用 操作符两次,可以把一个值转换为布尔型。

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

    核心

    为什么不要使用 eval

    eval 函数会在当前作用域中执行一段 JavaScript 代码字符串。

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

    但是 eval 只在被直接调用并且调用函数就是 eval 本身时,才在当前作用域中执行。

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

    译者注上面的代码等价于在全局作用域中调用 eval,和下面两种写法效果一样:

    +
    // 写法一:直接调用全局作用域下的 foo 变量
    +var foo = 1;
    +function test() {
    +    var foo = 2;
    +    window.foo = 3;
    +    return foo;
    +}
    +test(); // 2
    +foo; // 3
    +
    +// 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域
    +var foo = 1;
    +function test() {
    +    var foo = 2;
    +    eval.call(window, 'foo = 3');
    +    return foo;
    +}
    +test(); // 2
    +foo; // 3
    +

    任何情况下我们都应该避免使用 eval 函数。99.9% 使用 eval 的场景都有不使用 eval 的解决方案。

    +

    伪装的 eval

    +

    定时函数 setTimeoutsetInterval 都可以接受字符串作为它们的第一个参数。 +这个字符串总是在全局作用域中执行,因此 eval 在这种情况下没有被直接调用。

    +

    安全问题

    +

    eval 也存在安全问题,因为它会执行任意传给它的代码, +在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 eval 函数。

    +

    结论

    +

    绝对不要使用 eval,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。 +如果一些情况必须使用到 eval 才能正常工作,首先它的设计会受到质疑,这不应该是首选的解决方案, +一个更好的不使用 eval 的解决方案应该得到充分考虑并优先采用。

    +

    undefinednull

    JavaScript 有两个表示‘空’的值,其中比较有用的是 undefined

    +

    undefined 的值

    +

    undefined 是一个值为 undefined 的类型。

    +

    这个语言也定义了一个全局变量,它的值是 undefined,这个变量也被称为 undefined。 +但是这个变量不是一个常量,也不是一个关键字。这意味着它的可以轻易被覆盖。

    + +

    下面的情况会返回 undefined 值:

    +
      +
    • 访问未修改的全局变量 undefined
    • +
    • 由于没有定义 return 表达式的函数隐式返回。
    • +
    • return 表达式没有显式的返回任何内容。
    • +
    • 访问不存在的属性。
    • +
    • 函数参数没有被显式的传递值。
    • +
    • 任何被设置为 undefined 值的变量。
    • +
    +

    处理 undefined 值的改变

    +

    由于全局变量 undefined 只是保存了 undefined 类型实际的副本, +因此对它赋新值不会改变类型 undefined 的值。

    +

    然而,为了方便其它变量和 undefined 做比较,我们需要事先获取类型 undefined 的值。

    +

    为了避免可能对 undefined 值的改变,一个常用的技巧是使用一个传递到匿名包装器的额外参数。 +在调用时,这个参数不会获取任何值。

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // 局部作用域里的 undefined 变量重新获得了 `undefined` 值
    +
    +})('Hello World', 42);
    +

    另外一种达到相同目的方法是在函数内使用变量声明。

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

    这里唯一的区别是,在压缩后并且函数内没有其它需要使用 var 声明变量的情况下,这个版本的代码会多出 4 个字节的代码。

    + +

    null 的用处

    +

    JavaScript 中的 undefined 的使用场景类似于其它语言中的 null,实际上 JavaScript 中的 null 是另外一种数据类型。

    +

    它在 JavaScript 内部有一些使用场景(比如声明原型链的终结 Foo.prototype = null),但是大多数情况下都可以使用 undefined 来代替。

    +

    自动分号插入

    尽管 JavaScript 有 C 的代码风格,但是它强制要求在代码中使用分号,实际上可以省略它们。

    +

    JavaScript 不是一个没有分号的语言,恰恰相反上它需要分号来就解析源代码。 +因此 JavaScript 解析器在遇到由于缺少分号导致的解析错误时,会自动在源代码中插入分号。

    +
    var foo = function() {
    +} // 解析错误,分号丢失
    +test()
    +

    自动插入分号,解析器重新解析。

    +
    var foo = function() {
    +}; // 没有错误,解析继续
    +test()
    +

    自动的分号插入被认为是 JavaScript 语言最大的设计缺陷之一,因为它改变代码的行为。

    +

    工作原理

    +

    下面的代码没有分号,因此解析器需要自己判断需要在哪些地方插入分号。

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

    下面是解析器"猜测"的结果。

    +
    (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; // <- 插入分号, 改变了 return 表达式的行为
    +        { // 作为一个代码段处理
    +            foo: function() {} 
    +        }; // <- 插入分号
    +    }
    +    window.test = test; // <- 插入分号
    +
    +// 两行又被合并了
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- 插入分号
    +})(window); //<- 插入分号
    + +

    解析器显著改变了上面代码的行为,在另外一些情况下也会做出错误的处理

    +

    前置括号

    +

    在前置括号的情况下,解析器不会自动插入分号。

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

    上面代码被解析器转换为一行。

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

    log 函数的执行结果极大可能不是函数;这种情况下就会出现 TypeError 的错误,详细错误信息可能是 undefined is not a function

    +

    结论

    +

    建议绝对不要省略分号,同时也提倡将花括号和相应的表达式放在一行, +对于只有一行代码的 if 或者 else 表达式,也不应该省略花括号。 +这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。

    +

    其它

    setTimeoutsetInterval

    由于 JavaScript 是异步的,可以使用 setTimeoutsetInterval 来计划执行函数。

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // 返回一个大于零的数字
    +

    setTimeout 被调用时,它会返回一个 ID 标识并且计划在将来大约 1000 毫秒后调用 foo 函数。 +foo 函数只会被执行一次

    +

    基于 JavaScript 引擎的计时策略,以及本质上的单线程运行方式,所以其它代码的运行可能会阻塞此线程。 +因此没法确保函数会在 setTimeout 指定的时刻被调用。

    +

    作为第一个参数的函数将会在全局作用域中执行,因此函数内的 this 将会指向这个全局对象。

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // this 指向全局对象
    +        console.log(this.value); // 输出:undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    setInterval 的堆调用

    +

    setTimeout 只会执行回调函数一次,不过 setInterval - 正如名字建议的 - 会每隔 X 毫秒执行函数一次。 +但是却不鼓励使用这个函数。

    +

    当回调函数的执行被阻塞时,setInterval 仍然会发布更多的回调指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。

    +
    function foo(){
    +    // 阻塞执行 1 秒
    +}
    +setInterval(foo, 100);
    +

    上面代码中,foo 会执行一次随后被阻塞了一秒钟。

    +

    foo 被阻塞的时候,setInterval 仍然在组织将来对回调函数的调用。 +因此,当第一次 foo 函数调用结束时,已经有 10 次函数调用在等待执行。

    +

    处理可能的阻塞调用

    +

    最简单也是最容易控制的方案,是在回调函数内部使用 setTimeout 函数。

    +
    function foo(){
    +    // 阻塞执行 1 秒
    +    setTimeout(foo, 100);
    +}
    +foo();
    +

    这样不仅封装了 setTimeout 回调函数,而且阻止了调用指令的堆积,可以有更多的控制。 +foo 函数现在可以控制是否继续执行还是终止执行。

    +

    手工清空定时器

    +

    可以通过将定时时产生的 ID 标识传递给 clearTimeout 或者 clearInterval 函数来清除定时, +至于使用哪个函数取决于调用的时候使用的是 setTimeout 还是 setInterval

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

    清除所有定时器

    +

    由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。

    +
    // 清空"所有"的定时器
    +for(var i = 1; i < 1000; i++) {
    +    clearTimeout(i);
    +}
    +

    可能还有些定时器不会在上面代码中被清除(译者注如果定时器调用时返回的 ID 值大于 1000), +因此我们可以事先保存所有的定时器 ID,然后一把清除。

    +

    隐藏使用 eval

    +

    setTimeoutsetInterval 也接受第一个参数为字符串的情况。 +这个特性绝对不要使用,因为它在内部使用了 eval

    + +
    function foo() {
    +    // 将会被调用
    +}
    +
    +function bar() {
    +    function foo() {
    +        // 不会被调用
    +    }
    +    setTimeout('foo()', 1000);
    +}
    +bar();
    +

    由于 eval 在这种情况下不是被直接调用,因此传递到 setTimeout 的字符串会到全局作用域中执行; +因此,上面的回调函数使用的不是定义在 bar 作用域中的局部变量 foo

    +

    建议不要在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。

    +
    function foo(a, b, c) {}
    +
    +// 不要这样做
    +setTimeout('foo(1,2, 3)', 1000)
    +
    +// 可以使用匿名函数完成相同功能
    +setTimeout(function() {
    +    foo(1, 2, 3);
    +}, 1000)
    + +

    结论

    +

    绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, +这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。

    +

    另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。

    +
    \ No newline at end of file diff --git a/external/JavaScript-Garden/zhtw/index.html b/external/JavaScript-Garden/zhtw/index.html new file mode 100644 index 0000000..1166b94 --- /dev/null +++ b/external/JavaScript-Garden/zhtw/index.html @@ -0,0 +1,1196 @@ +JavaScript Garden

    簡介

    簡介

    JavaScript Garden 是一個不斷更新的文件,最主要是要去了解一些 Javascript 比較古怪的部份。 +給一些意見來防止遇到一些常見的錯誤和一些難以發現的問題,以及性能問題和不好的習慣。 +初學者也可以藉此去了解 Javascript 這項語言的特性。

    +

    JavaScript Garden 並 不是 要教導你 Javascript 的語言。 +如果要能夠理解這篇文章的內容,你需要事先學習 JavaScript 的基礎知識。 +在 Mozilla 開發者網路中有一系列非常棒的學習guide

    +

    作者

    +

    這個使用手冊是來自於 Stack Overflow 的使用者, Ivo Wetzel +(寫作) 和 Zhang Yi Jiang (設計)。

    +

    目前為 Tim Ruffles 維護此專案。

    +

    貢獻者

    + +

    繁體中文翻譯

    + +

    存放空間

    +

    JavaScript Garden 目前存放於 GitHub,但是 Cramer Development 讓我們有另一個存放位置在 JavaScriptGarden.info

    +

    許可

    +

    JavaScript Garden 是在 MIT license 許可協議下發佈,並存在於 +GitHub,如果你有發現錯誤或是打字上的錯誤 新增一個任務 或者發一個請求。 +你也可以在 StackOverflow 的 JavaScript room 上面找到我們。

    +

    物件

    物件的使用和屬性

    在 Javascript 中全部都是物件,除了 nullundefined

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

    一個常見的誤解就是字面值(literal)不是物件。這是因為 JavaScript 編譯器的一個錯誤,它試圖把 點操作符 解析為浮點數的字面值的一部分。

    +
    2.toString(); // 出錯: SyntaxError
    +

    有很多變通方法可以讓數字的字面值看起來像物件。

    +
    2..toString(); // 第二個點號可以正常解析
    +2 .toString(); // 注意點號前面的空格
    +(2).toString(); // 2 先被計算
    +

    物件做為數據類型

    +

    JavaScript 的物件可以作為 Hashmaps使用,主要用來保存命名的鍵與值的對應關係。

    +

    使用物件的字面語法 - {} - 可以創建一個簡單的物件。 這個新創建的物件繼承Object.prototype ,沒有任何 自定義屬性

    +
    var foo = {}; // 一個空的物件
    +
    +// 一個新的物件,有值為 12 的自定義屬性 'test'
    +var bar = {test: 12}; 
    +

    訪問屬性

    +

    有兩種方式來訪問物件的屬性,點操作或是中括號操作。

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

    兩種語法是相等的,唯一的差別是,使用中括號允許你動態的設定屬性,使用點操作不允許屬性為變數,否則會造成語法錯誤

    +

    刪除屬性

    +

    唯一刪除屬性的方式就是用 delete 操作符。設置屬性為 undefined 或是 null 只有刪除的屬性和值的關聯,沒有真的刪掉屬性

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

    上面的輸出結果有 bar undefinedfoo null +只有 baz 真正被刪除而已,所以從輸出結果中消失。

    +

    屬性名的語法

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

    物件的屬性名可以使用字符串或是普通的宣告。但是由於 JavaScript 編譯器存在一個錯誤設計。 +上面的兩種方式在 ECMAScript 5之前都會拋出 SyntaxError 的錯誤。

    +

    這個錯誤的原因是 delete 是 JavaScript 語言的一個 關鍵字 因此為了在更低的版本能執行最好用 string literal

    +

    Prototype

    JavaScript 不包含傳統繼承的模型,它使用的是原型模型。

    +

    儘管常常有人提及 JavaScript 的缺點,但基於原型的繼承模型比傳統繼承更強大。 +實現傳統的類繼承模型是很簡單。但是在 JavaScript 中實現原型的繼承模型則要困難很多。

    +

    由於 JavaScript 是唯一一個被廣泛使用的基於原型繼承的語言,所以我們必須要花時間來理解這兩者的不同。

    +

    第一個不同之處在於 JavaScript 使用 原型鏈 的繼承方式。

    + +
    function Foo() {
    +    this.value = 42;
    +}
    +Foo.prototype = {
    +    method: function() {}
    +};
    +
    +function Bar() {}
    +
    +// 設置 Bar 的 prototype 屬性為 Foo 的實例對象
    +Bar.prototype = new Foo();
    +Bar.prototype.foo = 'Hello World';
    +
    +// 修正 Bar.prototype.constructor 為 Bar 本身
    +Bar.prototype.constructor = Bar;
    +
    +var test = new Bar(); // 開啟一個新的實例
    +
    +// 原型鏈
    +test [instance of Bar]
    +    Bar.prototype [instance of Foo]
    +        { foo: 'Hello World', value: 42 }
    +        Foo.prototype
    +            { method: ... }
    +            Object.prototype
    +                { toString: ... /* etc. */ }
    +

    上面的例子中,物件 test 會繼承來自 Bar.prototypeFoo.prototype。因此它可以進入來自 Foo 原型的方法 method。 +同時它也可以訪問 那個 定義在原型上的 Foo 實例屬性 value

    +

    要注意的是 new Bar() 沒有 創立一個新的 Foo 實例,它重複利用的原本的 prototype。因此, Bar 的實例會分享到 相同value 屬性。

    + +

    屬性查詢

    +

    當查詢一個物件的屬性時,JavaScript 會 向上 查詢,直到查到指定名稱的屬性為止。

    +

    如果他查到原型鏈的頂部 - 也就是 Object.prototype - 但是仍然沒有指定的屬定,就會返回 undefined

    +

    原型屬性

    +

    當原型屬性用來建造原型鏈,它還是有可能去把 任意 類型的值給它

    +
    function Foo() {}
    +Foo.prototype = 1; // 無效
    +

    分派物件,在上面的例子中,將會動態的創建原型鏈。

    +

    效能

    +

    如果看在屬性在原型鏈的上端,對於查詢都會有不利的影響。特別的,試圖獲取一個不存在的屬性將會找遍所有原型鏈。

    +

    並且,當使用 迴圈找尋所有物件的屬性時,原型鏈上的 所有 屬性都會被訪問。

    +

    擴展 Native Prototype

    +

    一個經常發生的錯誤,那就是擴展 Object.prototype 或者是其他內建類型的原型物件。

    +

    這種技術叫做 monkey patching 並且會破壞 封裝。雖然被廣泛的應用到一些 Javascript 的架構,像是 Prototype , 但仍然沒有好的理由新增一個 非標準 的功能去搞亂內建型別。

    +

    擴展內置類型的 唯一 理由是為了和新的 JavaScript 保持一致,比如說 Array.forEach

    +

    總結

    +

    在寫複雜的程式碼的時候,要 充分理解 所有程式繼承的屬性還有原型鏈。 +還要提防原型鏈過長帶來的性能問題,並知道如何通過縮短原型鏈來提高性能。 +絕對 不要使用 native prototype 除非是為了和新的 JavaScript 引擎作兼容。

    +

    hasOwnProperty

    為了判斷一個物件是否包含 自定義 屬性而 不是 原型上的屬性,我們需要使用繼承 Object.prototypehasOwnProperty 方法。

    + +

    hasOwnProperty 是 JavaScript 中唯一一個處理屬性但是 找原型鏈的函式。

    +
    // 修改 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
    +

    只有 hasOwnProperty 給予正確的結果,這對進入物件的屬性很有效果,沒有 其他方法可以用來排除原型上的屬性,而不是定義在物件 自己 上的屬性。

    +

    hasOwnProperty 作為屬性

    +

    JavaScript 不會 保護 hasOwnProperty被占用,因此如果碰到存在這個屬性,就需要使用 外部hasOwnProperty 來獲取正確的結果。

    +
    var foo = {
    +    hasOwnProperty: function() {
    +        return false;
    +    },
    +    bar: 'Here be dragons'
    +};
    +
    +foo.hasOwnProperty('bar'); // 永遠返回 false
    +
    +// 使用其他對象的 hasOwnProperty,並將其上下設置為 foo
    +({}).hasOwnProperty.call(foo, 'bar'); // true
    +
    +// 也可以透過原生 Object prototype 的 hasOwnProperty 函數來達成目的
    +Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
    +

    結論

    +

    當檢查一個物件是否存在的時候, hasOwnProperty唯一 可用的方法。 +同時在使用 for in loop +建議使用 hasOwnProperty 避免 原型所帶來的干擾。

    +

    for in 迴圈

    就像其他的 in 操作符一樣, for in 循環也進入所有在物件中的屬性

    + +
    // 修改 Object.prototype
    +Object.prototype.bar = 1;
    +
    +var foo = {moo: 2};
    +for(var i in foo) {
    +    console.log(i); // 輸出兩個屬性:bar 和 moo
    +}
    +

    由於不可能改變 for in 本身的行為,因為有必要過濾出那些不希望在迴圈出現的屬性,這可以用 Object.prototype 原型上的 hasOwnProperty 的函數來完成。

    + +

    hasOwnProperty 來過濾

    +
    // foo 變數是上面範例中的
    +for(var i in foo) {
    +    if (foo.hasOwnProperty(i)) {
    +        console.log(i);
    +    }
    +}
    +

    這個版本的程式碼是唯一正確的寫法。由於我們使用了 hasOwnProperty,這次 輸出 moo。 +如果不使用這個程式碼在原型物件中(比如 Object.prototype)被擴展可能會出錯。

    +

    一個廣泛使用的舊函式庫 Prototype就擴展了原型的 JavaScript 物件。 +因此,當這個函式庫包含在頁面中時,不使用 hasOwnProperty 過濾的 for in 迴圈難免會出問題。

    +

    總結

    +

    推薦 總是 使用 hasOwnProperty。不要對程式碼的環境做任何假設,不要假設原生的對象是否被擴張。

    +

    函式

    函式的宣告和表達方式

    函式在 JavaScript 是第一等物件。這表示他們可以把函式當做值一樣傳遞。 +一個常見的用法是用 匿名函式 當做一個回傳去呼叫另一個函式,這是一種非同步函式

    +

    函式的宣告

    +
    function foo() {}
    +

    上面的函式在被執行之前會被 解析(hoisted),因此它可以在 任意 的地方都是 有宣告的 ,就算是在比這個函式還早呼叫。

    +
    foo(); // 可以執行,因為 foo 已經在運行前就被建立
    +function foo() {}
    +

    function 的表達式

    +
    var foo = function() {};
    +

    這個例子把一個 匿名 函式賦值給變數 foo

    +
    foo; // 'undefined'
    +foo(); // 錯誤: TypeError
    +var foo = function() {};
    +

    由於 var 已經宣告變數 foo 在所有的程式碼執行之前。 +所以 foo已經在程式運行前就已經被定義過了。 +但是因為賦值只會在運行時去執行,所以在程式碼執行前,foo 的值還沒被宣告所以為 undefined

    +

    命名函式的賦值表達式

    +

    另一個特殊狀況是將一個命名函式賦值給一個變數。

    +
    var foo = function bar() {
    +    bar(); // 可以運行
    +}
    +bar(); // 錯誤:ReferenceError
    +

    bar 不可以在外部的區域被執行,因為它只有在 foo 的函式內才可以執行。 +然而在 bar 內部還是可以看見。這是由於 JavaScript的 命名處理所致。 +函式名在函式內 可以使用。

    +

    this 的工作原理

    JavaScript 有一道完全不屬於其他語言處理 this 的處理機制。 +在 種不同的情況下, this 指向的各不相同

    +

    全域變數

    +
    this;
    +

    如果再全域範圍內使用 this,會指向 全域 的物件

    +

    呼叫一個函式

    +
    foo();
    +

    這裡 this 也會指向 全域 物件。

    + +

    呼叫一個方法

    +
    test.foo(); 
    +

    這個例子中, this 指向 test 物件。

    +

    呼叫一個建構函式

    +
    new foo(); 
    +

    如果函式傾向用 new 關鍵詞使用,我們稱這個函式為 建構函式。 +在函式內部, this 指向 新物件的創立

    +

    顯示的設置 this

    +
    function foo(a, b, c) {}
    +
    +var bar = {};
    +foo.apply(bar, [1, 2, 3]); // Array 會被擴展,如下所示
    +foo.call(bar, 1, 2, 3); // 傳遞參數 a = 1, b = 2, c = 3
    +

    當使用 function.prototype 上的 call 或只 apply 方法時,函式內的 this 將會被 顯示設置 為函式調用的第一個參數。

    +

    因此,在以上的例子中已不適用函式調用的原則,而且this會被設定指向bar

    + +

    常見誤解

    +

    儘管大部分的例子都合理,但第一個例子(譯者注: 應該是指前面呼叫一個函式的那個例子)可以被視為一個語言的不良設計,因為它從來就沒有實際用途。

    +
    Foo.method = function() {
    +    function test() {
    +        // this 設定為全域
    +    }
    +    test();
    +};
    +

    一個常見的誤解是 test 中的 this 指向 Foo 物件,但實際上並不是

    +

    為了在 test 中使用 Foo 物件,我們需要在 method 函式内部建立一個區域變數指向 Foo

    +
    Foo.method = function() {
    +    var that = this;
    +    function test() {
    +        // 這裡使用 that 而非 this
    +    }
    +    test();
    +};
    +

    that 只是普通的名字,不過這個名字常被用用來指向外部的 this。 在 閉包 一節,可以看到它(that)可以取代 this 傳遞。

    +

    在 ECMAScript 5 ,你可以使用 bind 結合匿名函式達到相同結果。

    +
    Foo.method = function() {
    +    var test = function() {
    +        // this 指向 Foo
    +    }.bind(this);
    +    test();
    +};
    +

    函式表達式

    +

    另一個在 JavaScript 中不會運作的就是 function aliasing,也就是函式賦值給一個變數。

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

    上例中,test 就像一個普通的函式被调用;因此,函式内的 this 將不再指向 someObject

    +

    雖然起初 this 的绑定特性似乎像是個壞主意,但事實上,它使得 原型繼承得以運作。

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

    method 被呼叫時,this 將會指向 Bar 的實體物件。

    +

    Closures 和 References

    JavaScript 有一個很重要的特徵就是 closures +因為有 Closures,所以作用域 永遠 能夠去訪問作用區間外面的變數。 +函數區間 是JavaScript 中唯一擁有自生作用域的結構,因此 Closures 的創立需要依賴函數

    +

    模仿私有變數

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

    這裡,Counter 返回兩個 Closures,函數 increment 還有 get。這兩個函數都維持著對外部作用域 Counter 的引用,因此總可以訪問作用域的變數 count

    +

    為什麼不可以在外部訪問私有變數

    +

    因為 Javascript 不可以 對作用域進行引用或賦值。因此外部的地方沒有辦法訪問 count 變數。 +唯一的途徑就是經過那兩個 Closures

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

    在上面的例子中 count 不會 改變到 Counter 裡面的 count 的值。因為 foo.hack 沒有在 那個 作用域內被宣告。它只有會覆蓋或者建立在一個 全域 的變數 count

    +

    在循環內的 Closures

    +

    一個常見的錯誤就是在 Closures 中使用迴圈,假設我們要使用每次迴圈中所使用的進入變數

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

    在上面的例子中它 不會 輸出數字從 09,但只會出現數字 10 十次。 +在 console.log 被呼叫的時候,這個 匿名 函數中保持一個 參考 到 i ,此時 for迴圈已經結束, i 的值被修改成了 10。 +為了要達到想要的結果,需要在每次創造 副本 來儲存 i 的變數。

    +

    避免引用錯誤

    +

    為了要有達到正確的效果,最好是把它包在一個 +匿名函數.

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

    匿名外部的函數被呼叫,並把 i 作為它第一個參數,此時函數內 e 變數就擁有了一個 i 的拷貝。 +當傳遞給 setTimeout 這個匿名函數執行時,它就擁有了對 e 的引用,而這個值 不會 被循環改變。 +另外有一個方法也可以完成這樣的工作,那就是在匿名函數中返回一個函數,這和上面的程式碼有同樣的效果。

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

    另外也可以透過 .bind 完成此工作,它可以將 this 及參數傳入函數內,行為就如同上面程式碼一樣。

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

    arguments 物件

    所有函數在 JavaScript 中都可以有個特別的參數 arguments。 +這個變數掌握了一列傳入函數中的參數

    + +

    arguments 物件 不是 一個 Array,雖然都有很多 Array 的語法 - 就像是 length 屬性 - 但是它沒有繼承來自 Array.prototype 事實上它繼承 object

    +

    由於這些原因,這 不可能 用 Array 的一些功能像是 pushpop或是 slicearguments。 +但是像 for 迴圈這些迴圈都是可以用的,如果真的需要使用一些標準的 Array 功能可以先把它轉成真的 Array 再去使用。

    +

    轉為 Array

    +

    下面的程式可以回傳一個新的 Array 包含所有的元素在 Arguments的物件中

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

    這種轉化方式比較 ,如果在追求效率的程式中,不建議使用這種作法。

    +

    傳遞參數

    +

    下面是建議用這種方式去傳參數到另一個函數

    +
    function foo() {
    +    bar.apply(null, arguments);
    +}
    +function bar(a, b, c) {
    +    // 在這裡做一些事情
    +}
    +

    另一個技巧是用 callapply 放在一起來創造一個更快的解綁定包裝器

    +
    function Foo() {}
    +
    +Foo.prototype.method = function(a, b, c) {
    +    console.log(this, a, b, c);
    +};
    +
    +// Create an unbound version of "method" 
    +// 輸入的參數: this, arg1, arg2...argN
    +Foo.method = function() {
    +
    +    // 結果: Foo.prototype.method.call(this, arg1, arg2... argN)
    +    Function.call.apply(Foo.prototype.method, arguments);
    +};
    +

    自動更新

    +

    Arguments 物件創造的 gettersetter 的函數方法,可以被視為原本函數的變數。

    +

    因此,改變了一個形式參將數會跟著改變對應的 arguments 的屬性,反之亦然。

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

    性能

    +

    arguments 總是會被宣告,但除了兩個情況,一個是在一個函式中或是在其中一個參入。而不論他是否有被使用。

    +

    getterssetter 會永遠被創造。然而,他們對任何性能都沒有影響,除非對它的屬性有多次的訪問

    + +

    然而會有一種情況來降低 JavaScript 引擎的效能。就是使用 arguments.callee

    +
    function foo() {
    +    arguments.callee; // 做一些在這個函數物件
    +    arguments.callee.caller; // 然後呼叫這個函數物件
    +}
    +
    +function bigLoop() {
    +    for(var i = 0; i < 100000; i++) {
    +        foo(); // 通常會在內聯
    +    }
    +}
    +

    在上面的程式中, foo 不再是一個單存的互聯函數 +因為它需要知道他自己和它的調用者。 +這不僅減低了它的性能,而且還破壞的封裝

    +

    強烈建議不要使用 arguments.callee 或是其他它的屬性

    + +

    建構函式

    JavaScript 中的建構函式和其他語言中的建構函式是不同的。 +用 new 的關鍵字方式調用的函式都被認為是建構函式。 +在建構函式內部 - 被呼叫的函式 - this 指向一個新建立的 objectprototype 這是一個新的物件一個被指向函式的 prototype 的建構函式。

    +

    如果被使用的函式沒有明顯的呼叫 return 的表達式,它會回傳一個隱性的 this 的新物件。

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

    在上面的例子中 Foo 建立一個建構函式,並設立一個 prototype 來創建一個新的物件叫 Foo.prototype。 +這個情況下它顯示的 return 一個表達式,但他 返回一個 Object

    +
    function Bar() {
    +    return 2;
    +}
    +new Bar(); // 返回一個新物件
    +
    +function Test() {
    +    this.value = 2;
    +
    +    return {
    +        foo: 1
    +    };
    +}
    +new Test(); // 回傳物件
    +

    如果 new 的關鍵字被忽略,函式就 不會 回傳一個新的物件。

    +
    function Foo() {
    +    this.bla = 1; // 獲取一個全域的參數
    +}
    +Foo(); // undefined
    +

    雖然上面有些情況也能正常運行,但是由於 JavaScript 中 this 的工作原理,這裡的 this 指向 全域對象

    +

    工廠模式

    +

    為了不使用 new 關鍵字,建構函式必須顯性的返回一個值。

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

    上面兩個呼叫 Bar 的方法回傳的值都一樣,一個新創建的擁有 method 屬性被返回,這裡創建了一個 Closure.

    +

    還有注意, new Bar()不會 改變返回物件的原型。 +因為建構函式的原型會指向剛剛創立的新物件,而在這裡的 Bar 沒有把這個新物件返回。 +在上面的例子中,使用或者不使用 new 關鍵字沒有什麼功能性的區別

    +

    通過工廠模式創建的新對象

    +

    常聽到建議 不要 使用 new,因為如果忘記如何使用它會造成錯誤。 +為了創建一個新的物件,我們可以用工廠方法,來創造一個新的物件在那個方法中。

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

    雖然上面的方式比起 new 的調用方式更不容易出錯,並且可以充分的使用 私有變數所帶來的便利,但是還是有一些不好的地方

    +
      +
    1. 會占用更多的記憶體,因為創建的物件 沒有 辦法放在在同一個原型上。
    2. +
    3. 為了要用繼承的方式,工廠方法需要複製所有的屬性或是把一個物件作為新的物件的原型。
    4. +
    5. 放棄原型鏈僅僅是因為防止遺漏 new 所帶來的問題,這與語言本身的思想鄉違背。
    6. +
    +

    結語

    +

    雖然遺漏 new 關鍵字可能會導致問題,但這並 不是 放棄只用原型的藉口。 +最終使用哪種方式取決於應用程式的需求,選擇一種程式語言風格並堅持下去才是最重要的。

    +

    作用域和命名空間

    儘管 JavaScript 支持一個大括號創建的程式碼,但並不支持塊級作用域。 +而僅僅支援 函式作用域

    +
    function test() { // 一個作用域
    +    for(var i = 0; i < 10; i++) { // 不是一個作用域
    +        // 算數
    +    }
    +    console.log(i); // 10
    +}
    + +

    JavaScript 中沒有寫示的命名空間定義,這代表著它所有定義的東西都是 全域共享 在同一個命名空間下。

    +

    每次引用一個變數,JavaScript 會向上找整個作用域直到找到這個變數為止。 +如果在全域中無法找到那個變數,它會拋出 ReferenceError 錯誤碼。

    +

    全域變數的壞處

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

    上面兩個腳本 不會 有同樣的效果。腳本 A 在 全域 空間定義了變數 foo,腳本 B 定義了 foo 在目前的區間內。

    +

    再次強調,上面的效果是 完全不同,不使用 var 會導致隱性的全域變數。

    +
    // 全域作用區
    +var foo = 42;
    +function test() {
    +    // 局部作用區
    +    foo = 21;
    +}
    +test();
    +foo; // 21
    +

    在函數 test 中部使用 var 會覆蓋到原本在外面的 foo。 +雖然看起來不是什麼大問題,但是當程式有幾千行的時候沒有使用 var 會照成難以追蹤的臭蟲。

    +
    // 全域作用域
    +var items = [/* some list */];
    +for(var i = 0; i < 10; i++) {
    +    subLoop();
    +}
    +
    +function subLoop() {
    +    // subLoop 的作用域
    +    for(i = 0; i < 10; i++) { // 缺少了 var
    +        // 做一些事情
    +    }
    +}
    +

    在外面的迴圈在呼叫第一次 subLoop 之後就會停止,因為 subLoop 全域變數中的 i 被覆蓋了。 +在第二次使用 for 迴圈的時候,使用 var 就可以避免這種錯誤。 +在宣告變數的時候 絕對不要 忘記 var,除非就是 希望他的效果 是取改變外部的作用域。

    +

    局部變數

    +

    在 javascript 中能用兩種方式來宣告局部變數。 +函式 參數和透過 var 來宣告變數。

    +
    // 全域變數
    +var foo = 1;
    +var bar = 2;
    +var i = 2;
    +
    +function test(i) {
    +    // 函式 test 內部的局部作用域
    +    i = 5;
    +
    +    var foo = 3;
    +    bar = 4;
    +}
    +test(10);
    +

    fooi 是它的局部變數在 test 函式中,但是在 bar 的賦值會覆蓋全區域的作用域內的同名變數。

    +

    變數宣告

    +

    JavaScript 會 提昇 變數宣告, 這代表著 varfunction 的圈告都會被提升到當前作用域的頂端。

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

    在上面的程式碼會被轉化在執行之前。 JavaScript 會把 var,和 function 宣告,放到最頂端最接近的作用區間

    +
    // var 被移到這裡
    +var bar, someValue; //  值等於 'undefined'
    +
    +// function 的宣告也被搬上來
    +function test(data) {
    +    var goo, i, e; // 沒有作用域的也被搬至頂端
    +    if (false) {
    +        goo = 1;
    +
    +    } else {
    +        goo = 2;
    +    }
    +    for(i = 0; i < 100; i++) {
    +        e = data[i];
    +    }
    +}
    +
    +bar(); // 出錯:TypeError , bar 還是 'undefined'
    +someValue = 42; // 賦值語句不會被提昇規則影響
    +bar = function() {};
    +
    +test();
    +

    沒有作用域區間不只會把 var 放到迴圈之外,還會使得 if 表達式更難看懂。

    +

    在一般的程式中,雖然 if 表達式中看起來修改了 全域變數 goo,但實際上在提昇規則被運用後,卻是在修改 局部變數

    +

    如果沒有提昇規則的話,可能會出現像下面的看起來會出現 ReferenceError 的錯誤。

    +
    // 檢查 SomeImportantThing 是否已經被初始化
    +if (!SomeImportantThing) {
    +    var SomeImportantThing = {};
    +}
    +

    但是它沒有錯誤,因為 var 的表達式會被提升到 全域作用域 的頂端。

    +
    var SomeImportantThing;
    +
    +// 有些程式,可能會初始化。
    +SomeImportantThing here, or not
    +
    +// 檢查是否已經被初始化。
    +if (!SomeImportantThing) {
    +    SomeImportantThing = {};
    +}
    +

    名稱解析順序

    +

    JavaScript 中所有的作用區,包括 全域作用域,都有一個特殊的名字 this, 在它們裡面被定義,指向當前的物件

    +

    函式作用域也有一個名稱叫做 arguments, 定義它們,其中包括傳到函式內的參數。

    +

    例如,它們開始試著進入到 foo 的作用域裡面, JavaScript 會依照下面的順序去查詢:

    +
      +
    1. 當作用域內是否有 var foo 的定義。
    2. +
    3. 函式形式參數是否有使用 foo 名稱定義。
    4. +
    5. 函式自身是否叫做 foo
    6. +
    7. 回溯到上一個層級然後再從第一個開始往下去查。
    8. +
    + +

    命名空間

    +

    只有一個全域作用域會導致常見的錯誤是命名衝突。在 JavaScript 中可以透過 匿名包裝器 來解決。

    +
    (function() {
    +    // 自己本身的匿名空間
    +
    +    window.foo = function() {
    +        // 對外公開的函式
    +    };
    +
    +})(); // 馬上執行這個匿名函式
    +

    匿名函式被認為是 表達式因此為了要可以調用,它們會先被執行。

    +
    ( // 小括號內的先被執行
    +function() {}
    +) // 回傳函數對象
    +() // 調用上面的執行結果
    +

    還有其他方式也可以像上面一樣調用函式的方式達到

    +
    !function(){}()
    ++function(){}()
    +(function(){}());
    +// and so on...
    +

    結語

    +

    建議最好是都用 匿名包裝器 來封裝你的程式碼在自己的命名區間內。這不僅是要防止命名衝突也可以使得程序更有模組化。

    +

    另外,全域變數是個 不好的 習慣,因為它會帶來錯誤和更難去維護。

    +

    陣列

    Array 迴圈和屬性

    雖然在 Javascript 中 Array 都是 Objects,但是沒有好的理由要使用他 +在 for in 的迴圈中。事實上有很多原因要避免使用 for in 在 Array 之中

    + +

    因為 for in 迴圈會使用hasOwnProperty,所以它會列舉所有在原型 Array 上的屬性,這會使得 Array 比原本的 for 迴圈慢上二十幾倍

    +

    迴圈

    +

    為了要達到最好的性能所以最好使用 for 迴圈來讀取一個 Array 裡面的數值。

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

    在上面的例子中利用 l = list.length 來處理 Array 的長度問題。

    +

    雖然 length 屬性是屬於 Array 中其中一個屬性,但是他在每次循環還是有一定的性能消耗。 +近期 Javascript 可能使用來解決在這上面的效率問題,但是在現在的引擎上還不一定有支援。

    +

    實際上,不使用暫存 Array 長度的方式比使用暫存的版本還要慢很多。

    +

    length 的屬性

    +

    length 屬性中的 getter 直接回傳在 Array 之中的程度,而 setter 可以用來 刪除 Array。

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

    在上面的例子可以看到,如果給的長度比較小他就會去刪除 Array 中的數值。如果比較大的話,他就會自己增加一些 undefined 的數值進去

    +

    結語

    +

    為了達到更好的效率,建議使用 for 迴圈還有暫存 length 的屬性。 +而 for in 迴圈則是會讓程式中有更多的錯誤和性能問題。

    +

    Array 的建構函式

    Array 的建構函式在處理參數上一直有模糊的地帶,所以建議使用 array的字面語法來使用 - [] - 來新增一個的Array

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

    在上面的範例 new Array(3) 當只有一個參數傳入到 Array 的建構函數 +且那個參數是一個數字,建構函數會回傳空值 +但是 Array 長度的屬性會變成跟那個參數一樣(以此範例來看他回傳的長度為 3) +注意 只有他長度的屬性會被設定,整個 Array裡面的數值都不會初始化

    +
    var arr = new Array(3);
    +arr[1]; // undefined
    +1 in arr; // false, 數值沒有被設定進去
    +

    被設定用來當做 Array 的長度只有少數情況使用 +先設定 Array 的長度可以用一下的範例來避免使用 for loop 的麻煩

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

    結語

    +

    Array 的建構函式需要避免,建議使用字面語法。因為他們比較簡短、也更增加閱讀性

    +

    類型

    相等與比較

    JavaScript 有兩個不同的方式來比較兩個物件是否相等。

    +

    等於操作符

    +

    等於操作符是由兩個等號組成: ==

    +

    JavaScript 是一個 弱類型 語言。這代表它會為了比較兩個值而做 強制類型轉換

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

    上面的表格可以看出來這些結果強制轉換類型,這也代表說用 == 是一個不好的習慣,因為它會很難追蹤問題由於它複雜的規則。

    +

    此外,也有效率上面的問題在強制轉換類型。 +例如說一個字串會被轉成數字來和別的數字做比較。

    +

    嚴格等於操作符

    +

    不像普通的等於操作符 === 不會做強制類型轉換。

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

    上面的結果比較清楚,也有利於程式碼的分析。如果這兩個操作數的類型不一樣都就不會相等,有助於它性能的提昇。

    +

    比較物件

    +

    雖然 ===== 都是等於操作符,但其中有一個操作數為物件時,它的行為就會不同。

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

    在這裡等於操作符比較 不是 值的相等,而是否是 相同 的身分。 +有點像 Python 的 is 和 C 中的指標。

    +

    結論

    +

    強烈建議使用 嚴格等於 +如果要轉換類型,應該要在 explicitly的時候轉換,而不是在語言本身用複雜的轉換規則。

    +

    typeof 操作符

    typeof 操作符 (和 +instanceof) 可能是最大的設計錯誤在 JavaScript,因為它幾乎不可能從它們那裡得到想要的結果。

    +

    雖然 instanceof 還是有一些限制上的使用, typeof 只有一個實際上的運用情形,但是 不是 用在檢查物件的類型。

    + +

    JavaScript 類型表格

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

    上面的表格中, Type 這一系列表示 typeof 的操作符的運算結果。可以看到,這個值的大多數情況下都返回物件。

    +

    Class 表示物件內部的屬性 [[Class]] 的值。

    + +

    為了獲取對象的 [[Class]],我們可以使用定義在 Object.prototype 上的方法 toString

    +

    物件的類定義

    +

    JavaScript 標準文檔只給出了一種獲取 [[Class]] 值的方法,那就是使用 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
    +

    上面的例子中,**Object.prototype.toStringthis的值來來調用被設置需要獲取 [[Class]] 值的物件。

    + +

    測試未定義變數

    +
    typeof foo !== 'undefined'
    +

    上面的例子確認 foo 是否真的被宣告。如果沒有定義會導致 ReferenceError 這是 typeof 唯一有用的地方

    +

    結語

    +

    為了去檢查一個物件,強烈建議去使用 Object.prototype.toString 因為這是唯一可以依賴的方式。 +正如上面所看到的 typeof 的事先返回值在標準文檔中未定義,因此不同的引擎可能不同。

    +

    除非為了檢測一個變數是否定義,我們應該避免使用 typeof 操作符。

    +

    instanceof 操作符

    instanceof 操作符用來比較兩個建構函數的操作數。只有在比較自定義的物件時才有意義。這和 typeof operator一樣用處不大。

    +

    比較定意義物件

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

    instanceof 比較內置類型

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

    有一點需要注意的, instanceof 不會在來自不同的上下文的物件運作(例如:瀏覽器中不同的文檔結構),因為它的建構函數不一樣。

    +

    In Conclusion

    +

    instanceof 操作符應該 用來比較同一個 JavaScript 上下文定意義的物件。 +正如 typeof操作符一樣,任何其他用法都要避免。

    +

    類型轉換

    JavaScript 是一個 弱類型 的程式語言,所以在 任何 情況下都可以 強制類型轉換

    +
    // 這些都是真
    +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
    +
    +// 下面都假
    +10 == 010;
    +10 == '-10';
    + +

    為了去避免上驗的事件發生,我們會用 嚴格等於操作符 這是強烈建議。 +因為它可以避免很多常見的問題,但 JavaScript 的弱類型系同仍然會導致一些其他問題。

    +

    內置類型的建構函式

    +

    內置類型(比如 NumberString)在被調用時,使用或不使用 new 的結果完全不同。

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

    使用內置類型 Number 作為建構函式會建造一個新的 Number 物件,而在不使用 new 關鍵字的 Number 函式更像是一個數字轉換器。

    +

    另外,在比較中引入物件的字面值會導致更加複雜的強制類型轉換。

    +

    最好的方式是比較值的 顯示 的轉換成最有可能的三種形態

    +

    轉換成字符串

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

    將一個值加上空字符串可以輕鬆轉為字符串類型。

    +

    轉換成一個數字

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

    使用 一元 的加號操作符,可以把字符串轉為數字。

    +

    轉換成一個 Bool

    +

    通過使用 操作符兩字,可以把一個值轉換為 Bool。

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

    核心

    為什麼不要使用 eval

    因為 eval 函數會在 Javascript 的區域性的區間執行那段程式碼。

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

    但是, eval 只接受直接的呼叫而且那個函數只能叫做 eval,才能在一個區段中執行。

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

    所有的 eval 都應該去比免試用。有 99.9% 的使用情況都可以 不必 使用到而達到同等效果。

    +

    偽裝的 eval

    +

    定時函數 setTimeoutsetInterval 都可以接受一個字串當做他們第一個參數。這些字串 永遠 都會在全域範圍內執行,因此在這種情況下 eval 沒有被直接的使用。

    +

    安全上的顧慮

    +

    eval 同樣有安全上的問題,因為所有的程式碼都可以被直接執行。 +而他不應去執行一串未知的字串或是來自不信任的來源。

    +

    結語

    +

    eval 應該永遠不要去只用它,任何的程式在被他執行後都有性能和安全上的考慮。如果有情況需要去使用他,他都不應該列為第一順位的解決方法。

    +

    應該有更好的方法能夠去使用,但是最好都不要去使用 eval

    +

    undefinednull

    JavaScript 中有兩個表示空值的方式, nullundefinedundefined是比較常用的一種。

    +

    undefined 的值

    +

    undefined 是一個值為 undefined 的類型。

    +

    語言中也定義了一個全域變數,它的值為 undefined,這個變數的被稱作 undefined 。 +這個變數 不是 一個常數,也不是一個關鍵字。這表示它的值可以被輕易的覆蓋。

    + +

    這裡有一些例子會回傳 undefined 的值:

    +
      +
    • 進入尚未修改的全域變數 undefined
    • +
    • 進入一個宣告但 尚未 初始化的變數。
    • +
    • return 表示式中沒有返回任何內容。
    • +
    • 呼叫不存在的屬性。
    • +
    • 函式參數沒有被傳遞數值。
    • +
    • 任何被被設定為 undefined 的變數。
    • +
    • 任何表達式中形式為 void(expression)
    • +
    +

    處理 undefined 值的改變

    +

    由於全域變數 undefined 只有保存 undefined 類型實際值的一個副本,指定了一個新的值並 不會 改變 undefined類型裡面的值。

    +

    為了避免去改變 undefined 的值,常用的技巧就是加上一個新的變數到 匿名包裝器。在使用的時候,這個參數不會接受任何的值。

    +
    var undefined = 123;
    +(function(something, foo, undefined) {
    +    // undefined 在區域區間內得到了 `undefined` 的值
    +
    +})('Hello World', 42);
    +

    另外一個可以得到同樣的效果就是在內部宣告一個變數

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

    唯一的不同就是在下者會多 4 個多 bytes 用來壓縮檔案,而且函數內也沒有其他需要使用 var

    +

    使用 null

    +

    JavaScript 中所使用的 undefined 類似別的語言中的 null , 但實際上在 JavaScript 中的 null 算是另外一個類型。

    +

    它在 JavaScript 有些可以使用的地方 (例如說宣告一個原型的終結,例如 Foo.prototype = null )。 +但是在大部分的時候可以用 undefined,來取代。

    +

    自動插入分號

    雖然 JavaScript 有 C 語言的語法,但是他不強制一定要加上分號。 +所以分號可以被忽略。

    +

    Javascript 並 不是 一個不需要分號的語言。實際上,它需要分號來讓程式碼更容易被理解。因此 Javascript 的編譯器中遇到了缺少分號的情形,它會自動的在程式碼中插入分號。

    +
    var foo = function() {
    +} // 編輯錯誤,因沒分號
    +test()
    +

    這時候編譯器在編輯的時候,會自動的加上分號,然後重新編輯。

    +
    var foo = function() {
    +}; // 沒有錯誤,編輯繼續
    +test()
    +

    自動的加入分號是被認為 最大 的設計缺陷之一,因為它能改變程式碼的行為。

    +

    工作原理

    +

    下面的程式碼中沒有使用任何的分號,所以編譯器需要去決定在哪些地方加入分號。

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

    下面的程式碼是編譯器 猜測 的結果。

    +
    (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; // <- 插入分號,改變了 return 的表達行為
    +        { // 作為另一個程式碼的處理
    +
    +            // 被當做一個獨立的函數來看
    +            foo: function() {} 
    +        }; // <- 插入分號
    +    }
    +    window.test = test; // <- 插入分號
    +
    +// 兩行又被合併
    +})(window)(function(window) {
    +    window.someLibrary = {}; // <- 插入分號
    +
    +})(window); //<- 插入分號
    + +

    編譯器在上面的程式碼中改變了原本程式碼的行為。在一些情況下,會做出 錯誤的行為

    +

    前置括號

    +

    在這種前置括號的情況下,編譯器 不會 自動的插入分號。

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

    上面的程式碼被編譯器轉為只有一行程式

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

    以上的範例中 log很大 的可能 不是 回傳一個函數。然而這個情況下會出現 TypeError 的錯誤或是會出現 undefined is not a function .

    +

    結語

    +

    建議永遠 不要 忽略分號。同樣的也建議大括號應在他對應的表達式在同一行。在 if... else...的表達式中也是如此,不應省略大括號。 +這個習慣可以不僅僅是讓你的程式更一致,也可以避免編譯器因為改變程式而出錯。

    +

    delete 控制符

    簡單來說,那是 不可能 去刪除一個全域變數,函式和其他東西在 JavaScript 中有一個 DontDelete 的屬性

    +

    全域和函式

    +

    當一個變數或是一個函式在一個全域範圍被定義或是在一個 funciton scope ,這些屬性可能是動態的物件或是全域的物件。這些特性有一系列的屬性。其中一個就是 DontDelete。 +在這些變數和函式的宣告都會有一個屬性叫 DontDelete,這會使得它無法被刪除。

    +
    // 全域變數
    +var a = 1; // DontDelete 屬性被建立
    +delete a; // false
    +a; // 1
    +
    +// normal function:
    +function f() {} // DontDelete 屬性被建立
    +delete f; // false
    +typeof f; // "function"
    +
    +// reassigning doesn't help:
    +f = 1;
    +delete f; // false
    +f; // 1
    +

    明確的屬性

    +

    明確的屬性可以被簡單的刪除。

    +
    // explicitly set property:
    +var obj = {x: 1};
    +obj.y = 2;
    +delete obj.x; // true
    +delete obj.y; // true
    +obj.x; // undefined
    +obj.y; // undefined
    +

    在上面的例子中, obj.xobj.y 可以被刪除是因為他們沒有 DontDelete 的屬性。 +所以下面的例子也可以這樣用。

    +
    // 可以運作,除了 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
    +

    這裡我們想要去刪除 athis 這裡指向一個全域的物件,和我們明確了地定義 a 是它的屬性,所以可以刪除它。

    +

    IE 有些臭蟲,所以上面的程式碼無法使用(至少 6~8)

    +

    函式的參數和內建

    +

    函式的普通參數,arguments object 還有一些內建的屬性都有 DontDelete 的建立

    +
    // function 參數和屬性
    +(function (x) {
    +
    +  delete arguments; // false
    +  typeof arguments; // "object"
    +
    +  delete x; // false
    +  x; // 1
    +
    +  function f(){}
    +  delete f.length; // false
    +  typeof f.length; // "number"
    +
    +})(1);
    +

    接受物件

    +

    控制符可以接受無法預測的物件。由於一些特別的情況,會允許它能夠 delete

    +

    結語

    +

    delete 控制符通常都有難以預料的行為,所以我們只可以安全的刪除顯著的屬性在普通的物件上。

    +

    其他

    setTimeoutsetInterval

    由於 Javascript 具有非同步的特性,因此可以用 setTimeoutsetInterval 來執行一個函式。

    + +
    function foo() {}
    +var id = setTimeout(foo, 1000); // returns a Number > 0
    +

    setTimeout 被呼叫,它會回傳一個 ID 標準並且 大約 1000 毫秒後在在去呼叫 foo 函式。 +foo 函式只會被執行 一次

    +

    基於 JavaScript 引擎的計時策略,以及基本的單線程運行的方式,所以其他的程式碼可以被阻塞。 +因此 沒法確保函式會在 setTimeout 指定的時可被調用。

    +

    第一個參數被函式呼叫的會在 全域物件 被呼叫,這代表 this在這個函式會指向全域物件。

    +
    function Foo() {
    +    this.value = 42;
    +    this.method = function() {
    +        // 指向全域
    +        console.log(this.value); // 會跑出 undefined
    +    };
    +    setTimeout(this.method, 500);
    +}
    +new Foo();
    + +

    setInterval 的堆調用

    +

    setTimeout 只會在函式上跑一次而已, setInterval - 則會在每隔 X 毫秒執行函式一次。但不鼓勵這種寫法。

    +

    當回傳函式的執行被阻塞時, setInterval 仍然會發佈更多的回傳函式。在很小的定時間隔情況像會使得回傳函式被堆疊起來。

    +
    function foo(){
    +    // 執行 1 秒
    +}
    +setInterval(foo, 100);
    +

    上面的程式中, foo 會執行一次然後被阻塞了一分鐘

    +

    foo 被阻塞的時候 setInterval 還是會組織將對回傳函式的調用。因此當第一次 foo 函式調用結束時,已經有 10 次函式的調用在等待執行。

    +

    處理可能被阻塞的調用

    +

    最簡單的解決方法,也是最容易控制的解決方法,就是在函式中使用 setTimeout

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

    這樣不只封裝了 setTimeout,也防止了堆疊的呼叫,還有給它更多的控制。 foo 可以去決定要不要繼續執行。

    +

    手動清理 Timeouts

    +

    清除 timeouts 所產生的 ID 標準傳遞給 clearTimeoutclearInterval 函式來清除定時, +至於使用哪個函式取決於調用的時候使用的是 setTimeout 還是 setInterval

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

    清除所有 Timeouts

    +

    由於沒有一個內建的方法可以一次清空所有的 timeouts 和 intervals,所以只有用暴力法來達到這樣的需求。

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

    可能還有一些定時器不會在上面的代碼中被清除,因此我們可以事先保存所有的定時器 ID,然後一把清除。

    +
    // clear "all" timeouts
    +var biggestTimeoutId = window.setTimeout(function(){}, 1),
    +i;
    +for(i = 1; i <= biggestTimeoutId; i++) {
    +    clearTimeout(i);
    +}
    +

    隱藏使用 eval

    +

    setTimeout and setInterval 也可以使用字串當作他們的第一個參數. +不過這個特性 絕對 不要使用, 因為在內部他將利用 eval 來實作。

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

    在這個範例中,由於 eval 沒有被直接呼叫,在 setTimeout 中被傳入的字串將會在 全域 範圍中被執行,因此,他將不會使用在 bar 區域的 foo

    +

    我們進一步建議 不要 用字串當作參數傳到會被 timeout 呼叫的函式中。

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

    結論

    +

    絕對 不要使用字串當作 setTimeoutsetInterval 參數。當參數要被當成呼叫的函式時,這絕對是 不好 的程式碼,相反的,利用 匿名函式 來完成這樣的行為。

    +

    此外,應該避免使用 setInterval,因為他將不會被 Javascript 給中斷。

    +
    \ No newline at end of file From 5410220fd8c16d3aa0b49d3156f4c172fcdf5594 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 27 Jun 2023 23:53:41 +0300 Subject: [PATCH 192/227] Removed broken symlink --- external/JavaScript-Garden/JavaScript-Garden | 1 - 1 file changed, 1 deletion(-) delete mode 120000 external/JavaScript-Garden/JavaScript-Garden diff --git a/external/JavaScript-Garden/JavaScript-Garden b/external/JavaScript-Garden/JavaScript-Garden deleted file mode 120000 index dcc6db5..0000000 --- a/external/JavaScript-Garden/JavaScript-Garden +++ /dev/null @@ -1 +0,0 @@ -./site \ No newline at end of file From 8239dbf18f6a67eebfebef7d2f60787bc60f6e96 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 28 Jun 2023 00:01:16 +0300 Subject: [PATCH 193/227] Updated broken JavaScript-Garden links --- README.md | 10 +++++----- external/JavaScript-Garden/BUILD.md | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 external/JavaScript-Garden/BUILD.md diff --git a/README.md b/README.md index b9600c1..eaee9a8 100755 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Read (_or preferably buy_) every single book of the excellent [**You Don't Know JS**](https://github.com/getify/You-Dont-Know-JS) series by [Kyle simpson](https://twitter.com/getify) **[MUST READ]** * Watch every video by [Douglas Crockford](https://www.youtube.com/results?search_query=douglas+crockford). * Take the full course ['JavaScript The Good Parts'](http://frontendmasters.com/courses/javascript-the-good-parts/#toc) by Douglas Crockford on [Front End Masters](http://frontendmasters.com) -* Get a really good understanding of [closures](http://bonsaiden.github.io/JavaScript-Garden/#function.closures) and [this](http://bonsaiden.github.io/JavaScript-Garden/#function.this) +* Get a really good understanding of [closures](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html#function.closures) and [this](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html#function.this) * [**JavaScript Closures are Super Simple!** (YouTube Video: 16 min)](https://www.youtube.com/watch?v=2cRjcXwsG0I) * [**JavaScript the Hard Parts: Closures, Scope, and Execution Context** (Student Approved Resource)](https://www.youtube.com/watch?v=ZVXrJ4dnUxM) * [**Understanding Functions and 'this'** By Bryan Hughes](https://www.youtube.com/watch?v=AOSYY1_np_4) @@ -97,7 +97,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [**The cost of JavaScript in 2019** By Addy Osmani](https://v8.dev/blog/cost-of-javascript-2019) * Check every tip on the [A Drip of JavaScript](http://designpepper.com/js-drip-archive/) list and subscribe to it. -* Check the _(archived version)_ of an old but good resource: [JavaScript Garden](https://web.archive.org/web/20160528154422/https://bonsaiden.github.io/JavaScript-Garden/). +* Check the _(archived version)_ of an old but good resource: [JavaScript Garden](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html). **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 and 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.* @@ -148,9 +148,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * Always use `let` or `const` when declaring variables. Avoid using `var`. * [Code using `"use strict"`](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * If you are using [VSCode Editor](https://code.visualstudio.com/) add the `//@ts-check` comment at the top of your code for an extra layer of checks via [TypeScript Linter](https://raw.githubusercontent.com/kostasx/LearnJavascript/master/img/ts-check.jpg) -* [Always use semicolons.](http://bonsaiden.github.io/JavaScript-Garden/#core.semicolon) // [**Not using semicolons??? Lonely Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) +* [Always use semicolons.](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html#core.semicolon) // [**Not using semicolons??? Lonely Island uses them everyday**](https://www.youtube.com/watch?v=M94ii6MVilw) * [Here's a little bit of **advice for the semicolon haters** (by Ben Alman)](http://benalman.com/news/2013/01/advice-javascript-semicolon-haters/) -* [Prefer using `===` over `==`](http://bonsaiden.github.io/JavaScript-Garden/#types.equality) +* [Prefer using `===` over `==`](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html#types.equality) * Always use curly braces * Comment your code * [Avoid/Reduce global variables](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) @@ -226,7 +226,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.2 GENERAL * [Essential JavaScript Links,A curated list by Eric Elliott and friends](https://github.com/ericelliott/essential-javascript-links) -* [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/) +* [JavaScript Garden](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html) * [Learn JavaScript Essentials (for all skill levels), by Eric Elliott](https://medium.com/javascript-scene/learn-javascript-b631a4af11f2) * [JavaScript, The Right Way](http://www.jstherightway.org/) * [Learn Javascript resources on MDN](https://developer.mozilla.org/en/learn/javascript) diff --git a/external/JavaScript-Garden/BUILD.md b/external/JavaScript-Garden/BUILD.md new file mode 100644 index 0000000..3ec9e7f --- /dev/null +++ b/external/JavaScript-Garden/BUILD.md @@ -0,0 +1,4 @@ +- Go to /GITHUB-FORKS/JavaScript-Garden +- git checkout main +- ./deploy.sh +- Copy all files, apart from: node_modules/, etc. \ No newline at end of file From 896d10ce0effd0f46a8510d1a50766759d9b57a2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 28 Jun 2023 00:05:01 +0300 Subject: [PATCH 194/227] Updated Eric Elliot link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaee9a8..df17f9f 100755 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.2 GENERAL -* [Essential JavaScript Links,A curated list by Eric Elliott and friends](https://github.com/ericelliott/essential-javascript-links) +* [Essential JavaScript Links,A curated list by Eric Elliott and friends](https://gist.github.com/ericelliott/d576f72441fc1b27dace/0cee592f8f8b7eae39c4b3851ae92b00463b67b9) * [JavaScript Garden](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html) * [Learn JavaScript Essentials (for all skill levels), by Eric Elliott](https://medium.com/javascript-scene/learn-javascript-b631a4af11f2) * [JavaScript, The Right Way](http://www.jstherightway.org/) From 8b46d624da2713c834498a6682e4d9e60269b0fb Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 28 Jun 2023 00:11:26 +0300 Subject: [PATCH 195/227] Fixing broken links and updated Contributors --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index df17f9f..59811b8 100755 --- a/README.md +++ b/README.md @@ -225,20 +225,19 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g #### 3.2 GENERAL +* [**Learn Javascript resources on MDN**](https://developer.mozilla.org/en/learn/javascript) * [Essential JavaScript Links,A curated list by Eric Elliott and friends](https://gist.github.com/ericelliott/d576f72441fc1b27dace/0cee592f8f8b7eae39c4b3851ae92b00463b67b9) * [JavaScript Garden](https://kostasx.github.io/LearnJavascript/external/JavaScript-Garden/index.html) * [Learn JavaScript Essentials (for all skill levels), by Eric Elliott](https://medium.com/javascript-scene/learn-javascript-b631a4af11f2) -* [JavaScript, The Right Way](http://www.jstherightway.org/) -* [Learn Javascript resources on MDN](https://developer.mozilla.org/en/learn/javascript) +* [JavaScript, The Right Way](https://www.jstherightway.org/) * [Dos and Donts: Best Practices When Learning JavaScript](https://www.youtube.com/watch?v=zILmbcIYnfw) -* [The site of Dr. Axel Rauschmayer](http://www.2ality.com/) -* [David Walsh's Blog](http://davidwalsh.name/) -* [DailyJS](http://dailyjs.com/) +* [The site of Dr. Axel Rauschmayer](https://www.2ality.com/) +* [David Walsh's Blog](https://davidwalsh.name/) * [Understand JavaScript Closures With Ease](http://javascriptissexy.com/understand-javascript-closures-with-ease/) -* [JavaScript the Right Way](http://jstherightway.org/) * [JavaScript and the Browser: Under the Hood, *by Ariya Hidayat*](https://speakerdeck.com/ariya/javascript-and-the-browser-under-the-hood) * [JSdo It: Share JavaScript, HTML5 and CSS.](http://jsdo.it/) * [Explore JavaScript: Popular and new libraries, top authors and trending discussions](https://kandi.openweaver.com/explore/javascript) +* [DailyJS](https://medium.com/dailyjs) > ES6 (EcmaScript 2015) @@ -1303,6 +1302,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g A big thanks to: +- [Peddi Anurag](https://github.com/AnuragAnalog) for spotting broken links - [Terence Grover](https://github.com/TerenceGrover) for spotting broken links and suggesting new ones - [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. From 866f1db68fe7390961bfa4c598f4f8cabb04575e Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 28 Jun 2023 10:42:18 +0300 Subject: [PATCH 196/227] Fixed Broken Links --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 59811b8..4073614 100755 --- a/README.md +++ b/README.md @@ -185,25 +185,26 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g * [JavaScript the Right Way](https://github.com/braziljs/js-the-right-way) * [**JavaScript Basics** A quick introduction to basic and important concepts of JavaScript](https://fkling.github.io/jsbasics/) * [JavaScript For Cats, *by Max Ogden*](http://jsforcats.com/) -* [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](http://www.thinkful.com/learn/javascript-best-practices-1/) * [JavaScript Guide, MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) * [Introduction to JavaScript - 24 Interactive Screencasts](https://scrimba.com/g/gintrotojavascript) * [Learn modern JavaScript - ES6 through Interactive Screencasts](https://scrimba.com/g/ges6) * [Introduction to ES6+ - 23 Interactive Screencasts](https://scrimba.com/g/gintrotoes6) -* [JAVASCRIPT FUNDAMENTALS, *by Azat Mardan*](http://gist.io/5955726) +* [JavaScript and Node Fundamentals: A Collection of Essential Basics, *by Azat Mardan*](https://pepa.holla.cz/wp-content/uploads/2016/07/JavascriptAndNodeFundametals.pdf) * [5 Tips to Become a Better JavaScript Developer, *by JUSTIN CHMURA*](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * [JS 101](http://www.teaching-materials.org/javascript/) -* [7 JavaScript Basics Many Developers Aren't Using (Properly)](http://tech.pro/tutorial/1453/7-javascript-basics-many-developers-aren-t-using-properly) * [5 Tips to Become a Better JavaScript Developer](http://justinchmura.com/2014/08/20/become-a-better-javascript-developer/) * [Things Every Javascript Developer Should Know, by Lubomir Vitol](http://www.devbattles.com/en/sand/post-1427-Things+Every+Javascript+Developer+Should+Know) * [JavaScript Idiosyncrasies](https://github.com/miguelmota/javascript-idiosyncrasies) * [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) * [Context or the "This" Keyword in JavaScript, *by Adam Breindel*](https://thenewcircle.com/s/post/1564/context_or_the_this_keyword_in_javascript_tutorial) -* [45 Useful JavaScript Tips, Tricks and Best Practices](http://modernweb.com/2013/12/23/45-useful-javascript-tips-tricks-and-best-practices/) +* [45 Useful JavaScript Tips, Tricks and Best Practices](https://modernweb.com/45-javascript-tips-tricks-practices/) * [JavaScript DevDocs](http://devdocs.io/javascript/) * [MDN JavaScript Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) * [**How to Become a Great JavaScript Developer***](http://blog.ustunozgur.com/javascript/programming/books/videos/2015/06/17/how_to_be_a_great_javascript_software_developer.html) * [FAQ for comp.lang.javascript](http://pointedears.de/scripts/faq/cljs/) +* **Archived Resources** _(links that are no longer available and only accessible via archive.org)_: + * [7 JavaScript Basics Many Developers Aren't Using (Properly)](https://web.archive.org/web/20150314040207/https://tech.pro/tutorial/1453/7-javascript-basics-many-developers-aren-t-using-properly) + * [Javascript: BEST PRACTICES PART 1, *by Christian Heilmann*](https://web.archive.org/web/20200615000455/https://www.thinkful.com/learn/javascript-best-practices-1/) / [Part 2](https://web.archive.org/web/20200802075107/https://www.thinkful.com/learn/javascript-best-practices-2/) ##### 3.1.1 COURSES [ UPDATED ] From 2e3c8de8748ff1e7d37891e93afda1110e3f424f Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 20 Jul 2023 20:03:14 +0300 Subject: [PATCH 197/227] Updated Redux section (thanks Mary!) --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4073614..a5e93a5 100755 --- a/README.md +++ b/README.md @@ -720,7 +720,22 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g React Redux
  • - ⭐ React Redux for Beginners (YouTube Series) + A cartoon intro to Redux by @linclark +
  • +
  • + ⭐ React Redux for Beginners by Codevolution +
  • +
  • + ⭐ Redux by Valentino Gagliardi (YouTube Playlist) +
  • +
  • + Easy Redux Tutorial: Adding Redux to a Simple React App (14') +
  • +
  • + React Redux Toolkit Playlist +
  • +
  • + Redux Tutorial (with Redux Toolkit)
  • Redux Origins: The History Behind the Popular Library From c01fc890c183e5f6f00ed13cb4baf60aa3bc6638 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 20 Jul 2023 20:04:19 +0300 Subject: [PATCH 198/227] Update contributors --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a5e93a5..c384212 100755 --- a/README.md +++ b/README.md @@ -1318,6 +1318,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g A big thanks to: +- [Mary Richelle](https://github.com/MaryRichelle) for reminding me to update the Redux section and fix some broken links - [Peddi Anurag](https://github.com/AnuragAnalog) for spotting broken links - [Terence Grover](https://github.com/TerenceGrover) for spotting broken links and suggesting new ones - [Richard Barnes](https://github.com/FenderStrat85) for suggesting the video **JavaScript the Hard Parts: Closures, Scope, and Execution Context**. From 4afcde147193246ac32471886fbf4f610c6cf7b9 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 20 Jul 2023 20:05:51 +0300 Subject: [PATCH 199/227] Update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c384212..e03f0e5 100755 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - [3.4.3 Angular & AngularJS](#) - [3.4.4 Ember](#) - [3.4.5 Knockout.js](#) - - [3.4.6 React.js](#) + - [3.4.6 React.js](#reactjs) - [3.5 Books](#35-books) - [3.6 Online Books](#36-online-books) - [3.7 Video & Audio](#37-video--audio) @@ -664,9 +664,9 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g -
    +
    - 3.4.6 REACT.JS [ UPDATED ] + 3.4.6 REACT.JS & Redux [ UPDATED ]
      From e7884610d149e9b90dedc4c783425a89616504bc Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 20 Jul 2023 20:06:30 +0300 Subject: [PATCH 200/227] Update ToC --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e03f0e5..f04ec30 100755 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g - [3.4.3 Angular & AngularJS](#) - [3.4.4 Ember](#) - [3.4.5 Knockout.js](#) - - [3.4.6 React.js](#reactjs) + - [3.4.6 React.js & Redux](#reactjs) - [3.5 Books](#35-books) - [3.6 Online Books](#36-online-books) - [3.7 Video & Audio](#37-video--audio) From 35e973f91a9da612727421a7aa8d248855c4ba96 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Thu, 20 Jul 2023 20:16:01 +0300 Subject: [PATCH 201/227] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f04ec30..c83b1b3 100755 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ This list was inspired by (a now defunct) [discussion](http://www.linkedin.com/g --- -**About me**: [GitHub](https://github.com/kostasx/) | [Twitter](https://twitter.com/kostas_mns) | [StackOverflow](https://stackoverflow.com/users/4861760/kostasx) | [LinkedIn](https://www.linkedin.com/in/kostas-minaidis/) | [PlethoraThemes](https://plethorathemes.com/author/kostasx/) +**About me**: [GitHub](https://github.com/kostasx/) | [Twitter](https://twitter.com/kostas_mns) | [StackOverflow](https://stackoverflow.com/users/4861760/kostasx) | [LinkedIn](https://www.linkedin.com/in/kostas-minaidis/) | [CodeGrepper](https://www.grepper.com/profile/kostas-minaidis) | [PlethoraThemes](https://plethorathemes.com/author/kostasx/) --- From 153fff2591a995de96f4ca99d4f1445204c3b06b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 24 Jul 2023 23:16:51 +0300 Subject: [PATCH 202/227] Create README.md --- teach/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 teach/README.md diff --git a/teach/README.md b/teach/README.md new file mode 100644 index 0000000..5157ed3 --- /dev/null +++ b/teach/README.md @@ -0,0 +1,3 @@ +# Teaching JavaScript (and Computer Science) + +[Work in progress] From 0d375ffa450777a7b0c774312c9ddea607c866aa Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 24 Jul 2023 23:24:17 +0300 Subject: [PATCH 203/227] Update README.md --- teach/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/teach/README.md b/teach/README.md index 5157ed3..7789e92 100644 --- a/teach/README.md +++ b/teach/README.md @@ -1,3 +1,7 @@ # Teaching JavaScript (and Computer Science) [Work in progress] + +## Resources + +- [**@teachcode:** Supporting K12 educators teaching computer science](https://twitter.com/teachcode) From 819ca25ab9ed498536a0aa1a9de059d275b92fee Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Mon, 24 Jul 2023 23:26:55 +0300 Subject: [PATCH 204/227] Update README.md --- teach/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/teach/README.md b/teach/README.md index 7789e92..ded2670 100644 --- a/teach/README.md +++ b/teach/README.md @@ -4,4 +4,5 @@ ## Resources +- [Teacher Tips (code.org)](https://www.youtube.com/playlist?list=PLzdnOPI1iJNcc7Q-PvUVqV249l2JzhJes) - [**@teachcode:** Supporting K12 educators teaching computer science](https://twitter.com/teachcode) From f317a7ee7afa328c949e2d5d9b1811516d49222d Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 26 Jul 2023 22:53:34 +0300 Subject: [PATCH 205/227] Update README.md --- teach/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/teach/README.md b/teach/README.md index ded2670..a80d363 100644 --- a/teach/README.md +++ b/teach/README.md @@ -6,3 +6,4 @@ - [Teacher Tips (code.org)](https://www.youtube.com/playlist?list=PLzdnOPI1iJNcc7Q-PvUVqV249l2JzhJes) - [**@teachcode:** Supporting K12 educators teaching computer science](https://twitter.com/teachcode) +- [5 Things Every Presenter Needs To Know About People](https://www.youtube.com/watch?v=WJUblvGfW6w) From 5409959e36ddffb90231a6059b36329a2f46bd24 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 8 Aug 2023 11:59:14 +0300 Subject: [PATCH 206/227] Added resources related to YT video --- .../intro.to.typescript.greek/resources.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 deconstructing/resources/youtube/intro.to.typescript.greek/resources.md diff --git a/deconstructing/resources/youtube/intro.to.typescript.greek/resources.md b/deconstructing/resources/youtube/intro.to.typescript.greek/resources.md new file mode 100644 index 0000000..fe9a9bb --- /dev/null +++ b/deconstructing/resources/youtube/intro.to.typescript.greek/resources.md @@ -0,0 +1,46 @@ +## Fixing the `premium` type issue ("golden" | "bronze") (Timestamp: 01:10:15) + +```ts +type premiumTag = "golden" | "bronze" + +interface IPAccount { + premium: premiumTag +} + +class Premium implements IPAccount { + // Way 1) + premium = "golden" as premiumTag; + + // Way 2) + premium:premiumTag = "golden"; +} +``` + +## Fixing the Premium Class type (Timestamp: 01:13:00) + +The correct code is the following: + +```ts +interface Account { + balance:number +} + +type premiumTag = "golden" | "bronze" + +interface IPAccount { + premium: premiumTag; +} +type ISpecial = Account & IPAccount; + +class Premium implements ISpecial /* NOT: implements IPAccount*/ { + balance:number; + premium:premiumTag; + constructor(balance:number, premium:premiumTag){ + this.balance = balance; + this.premium = premium; + } +} + +const premium = new Premium(100, "golden"); +// premium has an inferred type `Premium` which in turn implements `ISpecial` +``` \ No newline at end of file From c89775a6292f1d3dd4006b9972d5886406b54e74 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 8 Aug 2023 12:00:46 +0300 Subject: [PATCH 207/227] Updated location --- .../youtube/intro.to.typescript.greek/resources.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {deconstructing/resources => resources}/youtube/intro.to.typescript.greek/resources.md (100%) diff --git a/deconstructing/resources/youtube/intro.to.typescript.greek/resources.md b/resources/youtube/intro.to.typescript.greek/resources.md similarity index 100% rename from deconstructing/resources/youtube/intro.to.typescript.greek/resources.md rename to resources/youtube/intro.to.typescript.greek/resources.md From 0ec2a256462d6220da3f25a74188360c7130bb46 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 8 Aug 2023 12:01:59 +0300 Subject: [PATCH 208/227] Update resources.md --- resources/youtube/intro.to.typescript.greek/resources.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/youtube/intro.to.typescript.greek/resources.md b/resources/youtube/intro.to.typescript.greek/resources.md index fe9a9bb..c9d292b 100644 --- a/resources/youtube/intro.to.typescript.greek/resources.md +++ b/resources/youtube/intro.to.typescript.greek/resources.md @@ -1,3 +1,7 @@ +# Related Video "Εισαγωγή στην TypeScript" + +URL: [https://www.youtube.com/watch?v=Er_a-NwKMws](https://www.youtube.com/watch?v=Er_a-NwKMws) + ## Fixing the `premium` type issue ("golden" | "bronze") (Timestamp: 01:10:15) ```ts @@ -43,4 +47,4 @@ class Premium implements ISpecial /* NOT: implements IPAccount*/ { const premium = new Premium(100, "golden"); // premium has an inferred type `Premium` which in turn implements `ISpecial` -``` \ No newline at end of file +``` From ea9f1c8e3c48d1422c7b27b86cc0475dea76e156 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 8 Aug 2023 12:02:48 +0300 Subject: [PATCH 209/227] Update resources.md --- resources/youtube/intro.to.typescript.greek/resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/youtube/intro.to.typescript.greek/resources.md b/resources/youtube/intro.to.typescript.greek/resources.md index c9d292b..45503c9 100644 --- a/resources/youtube/intro.to.typescript.greek/resources.md +++ b/resources/youtube/intro.to.typescript.greek/resources.md @@ -1,4 +1,4 @@ -# Related Video "Εισαγωγή στην TypeScript" +# Σημειώσεις & διορθώσεις για το video "Εισαγωγή στην TypeScript" URL: [https://www.youtube.com/watch?v=Er_a-NwKMws](https://www.youtube.com/watch?v=Er_a-NwKMws) From a01a90a7c54736f7d773354d6a64a19454fb37b3 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Tue, 8 Aug 2023 12:13:02 +0300 Subject: [PATCH 210/227] Updated YT resources --- resources/youtube/intro.to.typescript.greek/resources.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/youtube/intro.to.typescript.greek/resources.md b/resources/youtube/intro.to.typescript.greek/resources.md index 45503c9..1c932c7 100644 --- a/resources/youtube/intro.to.typescript.greek/resources.md +++ b/resources/youtube/intro.to.typescript.greek/resources.md @@ -48,3 +48,7 @@ class Premium implements ISpecial /* NOT: implements IPAccount*/ { const premium = new Premium(100, "golden"); // premium has an inferred type `Premium` which in turn implements `ISpecial` ``` + +## Timestamp: 01:18:55 + +The correct syntax for the type on the 3rd example should be `object[]` and not `{}[]` From 205d74c48041455a5b2d03715fa48e9d3ec98243 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 9 Aug 2023 15:48:57 +0300 Subject: [PATCH 211/227] Create README.md --- resources/youtube/intro.to.react.custom.hooks/source/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/README.md diff --git a/resources/youtube/intro.to.react.custom.hooks/source/README.md b/resources/youtube/intro.to.react.custom.hooks/source/README.md new file mode 100644 index 0000000..419f96e --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/README.md @@ -0,0 +1,3 @@ +# Introduction to React Custom Hooks + +Date: 06.2022 From 09a7bfa3b777fe6eccb0415f23731434d3e433ec Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 9 Aug 2023 16:05:13 +0300 Subject: [PATCH 212/227] Added resources for React Custom Hooks video --- .../source/NOTES.md | 0 .../source/README.md | 2 +- .../source/package.json | 29 +++++ .../source/public/index.html | 29 +++++ .../source/src/Custom.Hooks.jsx | 111 ++++++++++++++++++ .../source/src/React.Query.jsx | 45 +++++++ .../source/src/index.js | 12 ++ .../source/src/styles.css | 8 ++ 8 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/NOTES.md create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/package.json create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/public/index.html create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/src/Custom.Hooks.jsx create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/src/React.Query.jsx create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/src/index.js create mode 100644 resources/youtube/intro.to.react.custom.hooks/source/src/styles.css diff --git a/resources/youtube/intro.to.react.custom.hooks/source/NOTES.md b/resources/youtube/intro.to.react.custom.hooks/source/NOTES.md new file mode 100644 index 0000000..e69de29 diff --git a/resources/youtube/intro.to.react.custom.hooks/source/README.md b/resources/youtube/intro.to.react.custom.hooks/source/README.md index 419f96e..145dc2e 100644 --- a/resources/youtube/intro.to.react.custom.hooks/source/README.md +++ b/resources/youtube/intro.to.react.custom.hooks/source/README.md @@ -1,3 +1,3 @@ -# Introduction to React Custom Hooks +# Introduction to React Custom Hooks (react-c12-29062022) Date: 06.2022 diff --git a/resources/youtube/intro.to.react.custom.hooks/source/package.json b/resources/youtube/intro.to.react.custom.hooks/source/package.json new file mode 100644 index 0000000..aed85db --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/package.json @@ -0,0 +1,29 @@ +{ + "name": "react-c12-29062022", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "src/index.js", + "dependencies": { + "react": "18.1.0", + "react-dom": "18.1.0", + "react-query": "3.39.1", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} \ No newline at end of file diff --git a/resources/youtube/intro.to.react.custom.hooks/source/public/index.html b/resources/youtube/intro.to.react.custom.hooks/source/public/index.html new file mode 100644 index 0000000..9272d00 --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/public/index.html @@ -0,0 +1,29 @@ + + + + + + + + + + React App + + + + +
      + + diff --git a/resources/youtube/intro.to.react.custom.hooks/source/src/Custom.Hooks.jsx b/resources/youtube/intro.to.react.custom.hooks/source/src/Custom.Hooks.jsx new file mode 100644 index 0000000..b5f8ab6 --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/src/Custom.Hooks.jsx @@ -0,0 +1,111 @@ +import { useState, useEffect } from 'react'; +console.clear(); +function useFetch( URL ){ + + const [ data, setData ] = useState(null); + const [ error, setError ] = useState(false); + const [ isLoading, setLoading ] = useState(false); + const [ counter, setCounter ] = useState(0); + + useEffect(()=>{ + // Ajax call => set State + async function fetchTodos(){ + try { + setLoading( true ); + const res = await fetch( URL ); + const data = await res.json(); + // Batch Updates: in multiple set states of the same state variable + // React keeps the last one: + // setCounter(1) + // setCounter(2) + // setCounter(3) // <= React will keep this one + setData( data ); // <= Two State Changes in One + } catch (e){ + setError(e); + } finally { + setLoading(false); // <= Two State Changes in One + } + } + fetchTodos(); + },[]); + + return { + data, + isLoading, + error + } +} +function Dashboard(){ + const [ todos, setTodos ] = useState([]); + const [ error, setError ] = useState(false); + const [ isLoading, setLoading ] = useState(false); + console.log("Dashboard()"); + useEffect(()=>{ + // Ajax call => set State + async function fetchTodos(){ + try { + setLoading( true ); + const res = await fetch("https://jsonplaceholder.typicode.com/todos?_delay=3000&debug=" + Math.random()); + const data = await res.json(); // VM1062:2 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 1 + // const data = await res.text(); + console.log( "data", data ); + setTodos( data.slice(0,5) ); + } catch (e){ + console.log("Error:", e.message); + setError(e); + } finally { + setLoading(false); + } + } + fetchTodos(); + },[]); + + if ( error ){ + return Error: Could not reach server + } + + return ( + <> +

      Dashboard

      + { isLoading && Loading... } + {/* { error && Error: Could not reach server } */} +
        + {todos.map( todo =>{ + return ( +
      • + { todo.title } +
      • + ) + })} +
      + + ) +} +function Panel(){ + console.log("Panel()"); + const { counter, data: todos, isLoading, error } = useFetch( "https://jsonplaceholder.typicode.com/todos?_delay=3000&debug=" + Math.random() ); + // ALWAYS TEST FOR NEGATIVE OUTCOMES & EDGE CASES: + // const { data } = useFetch( "https://jsonplaceholder.typicode.com/todos/1?_delay=3000&debug=" + Math.random() ); + + return ( + <> +

      Panel { counter }

      + {/* { data && data.title } */} + { isLoading && Loading... } + { error && Error: { error.message } } + { todos && todos.slice(0,5).map( todo =>{ + return

      { todo.title }

      + })} + + ) +} + +export default function App() { + return ( + <> +

      React

      + + + + ); +} diff --git a/resources/youtube/intro.to.react.custom.hooks/source/src/React.Query.jsx b/resources/youtube/intro.to.react.custom.hooks/source/src/React.Query.jsx new file mode 100644 index 0000000..0b0bf26 --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/src/React.Query.jsx @@ -0,0 +1,45 @@ +import { QueryClientProvider, QueryClient, useQuery, useMutation } from 'react-query' +// npm install react-query +console.clear(); +const queryClient = new QueryClient(); + +const fetchJoke = async ()=>{ + const res = await fetch("https://api.chucknorris.io/jokes/random"); + const json = await res.json(); + return json; +} + +function Dashboard(){ + // useQuery( queryKey, queryFn ); + const { data, isLoading, error } = useQuery( + "joke", + fetchJoke, { + retry: 3, + refetchOnWindowFocus: true + }); + + if ( isLoading ){ + return

      Loading...

      + } + + if ( error ){ + return

      Error: { error.message }

      + } + + return ( +
      +

      Dashboard

      + { data.value } +
      + ) +} + +export default function ReactQuery(){ + return ( + +

      React Query

      + + {/* */} +
      + ) +} \ No newline at end of file diff --git a/resources/youtube/intro.to.react.custom.hooks/source/src/index.js b/resources/youtube/intro.to.react.custom.hooks/source/src/index.js new file mode 100644 index 0000000..0b3ae4d --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/src/index.js @@ -0,0 +1,12 @@ +import { createRoot } from "react-dom/client"; +import "./styles.css"; + +// Custom Hooks: useFetch() +// import App from "./Custom.Hooks"; + +// React Query +import App from "./React.Query"; + +const rootElement = createRoot(document.getElementById("root")); +rootElement.render( ); + diff --git a/resources/youtube/intro.to.react.custom.hooks/source/src/styles.css b/resources/youtube/intro.to.react.custom.hooks/source/src/styles.css new file mode 100644 index 0000000..664f0ad --- /dev/null +++ b/resources/youtube/intro.to.react.custom.hooks/source/src/styles.css @@ -0,0 +1,8 @@ +body { + width: 100vw; + min-height: 100vh; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + background: #00d8ff; + color: white; + +} \ No newline at end of file From 06bfbf89d0fb6e2044f2f839c6ea916824867b1a Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 20 Aug 2023 15:06:48 +0300 Subject: [PATCH 213/227] Create README.md --- .../coding.an.interactive.string.length.counter/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 resources/youtube/coding.an.interactive.string.length.counter/README.md diff --git a/resources/youtube/coding.an.interactive.string.length.counter/README.md b/resources/youtube/coding.an.interactive.string.length.counter/README.md new file mode 100644 index 0000000..9681864 --- /dev/null +++ b/resources/youtube/coding.an.interactive.string.length.counter/README.md @@ -0,0 +1 @@ +# Coding an Interactive String Length Counter in JavaScript From a5d025cd4e1ee0727871cc2e178af0af06ae5621 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 20 Aug 2023 16:46:14 +0300 Subject: [PATCH 214/227] Create index.html --- .../index.html | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 resources/youtube/coding.an.interactive.string.length.counter/index.html diff --git a/resources/youtube/coding.an.interactive.string.length.counter/index.html b/resources/youtube/coding.an.interactive.string.length.counter/index.html new file mode 100644 index 0000000..d82d0c7 --- /dev/null +++ b/resources/youtube/coding.an.interactive.string.length.counter/index.html @@ -0,0 +1,103 @@ + + + + + + Interactive Playground + + + + +
      +
      + + const someString = "Hello world!"; +
      + console.log(someString.length); +
      +
      +
      +
      +
      + String: +
      +
      +
      + String .length: +
      +
      +
      +

      Console output:

      + +
      + + From baa2d33004e9c0c81917792d2f694727b412ccc2 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 20 Aug 2023 16:47:55 +0300 Subject: [PATCH 215/227] Update index.html --- .../index.html | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/resources/youtube/coding.an.interactive.string.length.counter/index.html b/resources/youtube/coding.an.interactive.string.length.counter/index.html index d82d0c7..29fdeaa 100644 --- a/resources/youtube/coding.an.interactive.string.length.counter/index.html +++ b/resources/youtube/coding.an.interactive.string.length.counter/index.html @@ -97,6 +97,39 @@

      Console output:

      + + From 66549b6c0b6f8eed92980136c2a8907cb6fe4364 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 20 Aug 2023 17:09:26 +0300 Subject: [PATCH 216/227] Update README.md --- .../README.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/resources/youtube/coding.an.interactive.string.length.counter/README.md b/resources/youtube/coding.an.interactive.string.length.counter/README.md index 9681864..bae419d 100644 --- a/resources/youtube/coding.an.interactive.string.length.counter/README.md +++ b/resources/youtube/coding.an.interactive.string.length.counter/README.md @@ -1 +1,53 @@ # Coding an Interactive String Length Counter in JavaScript + +Code accompanying this YouTube video: []() + +--- +Created: 20/08/2023 +Duration: 57:47 +Level: Beginner +--- + +## Description + +In this video, we will code an interactive playground where the user can update the value of a string and the string characters along with the string count are automatically displayed in a nice looking UI. It is intended to help someone implement a simple web app from scratch using CSS and JavaScript. + +## Topics covered / What you'll learn: + +- String methods and the length property +- String.prototype.split +- CSS Selectors +- Emmet Abbreviations +- Chrome DevTools, using the Inspector +- Hidden spaces in HTML +- The 'contenteditable' HTML attribute +- Handling the 'input' event +- Flexbox layouts +- Using innerHTML + +## Resources & References: + +[Live Codesandbox](https://codesandbox.io/s/github/kostasx/LearnJavascript/tree/master/resources/youtube/coding.an.interactive.string.length.counter) + +[Source code](https://github.com/kostasx/LearnJavascript/blob/master/resources/youtube/coding.an.interactive.string.length.counter/index.html) + +- Quickly open a new Codepen: [pen.new](pen.new) + +- [Emmet Abbreviations](https://docs.emmet.io/abbreviations/) + +- [System Font Stack](https://css-tricks.com/snippets/css/system-font-stack/) + +- [contenteditable HTML attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable +) + +- [VSCode Editor Online](https://vscode.dev/) + +**CSS Selectors:** + +- [Child Combinator `>`](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator +) + +- [:last-child](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child) + +- [:nth-child()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child +) From fa278ad1e4f1e053fb9972e1b64a5495c0f7ae01 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sun, 20 Aug 2023 17:09:56 +0300 Subject: [PATCH 217/227] Update README.md --- .../coding.an.interactive.string.length.counter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/youtube/coding.an.interactive.string.length.counter/README.md b/resources/youtube/coding.an.interactive.string.length.counter/README.md index bae419d..d4fabf8 100644 --- a/resources/youtube/coding.an.interactive.string.length.counter/README.md +++ b/resources/youtube/coding.an.interactive.string.length.counter/README.md @@ -1,6 +1,6 @@ # Coding an Interactive String Length Counter in JavaScript -Code accompanying this YouTube video: []() +Code accompanying this YouTube video: [Coding an Interactive String Counter in JavaScript (Beginner)](https://youtu.be/tw4IBxkOQ40) --- Created: 20/08/2023 From f33a3b7b9edafe78a81d225b2cf82685eb8acf12 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:15:38 +0300 Subject: [PATCH 218/227] Create README.md --- .../introduction.to.React.Testing.Library/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 resources/youtube/introduction.to.React.Testing.Library/README.md diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md new file mode 100644 index 0000000..e039bb3 --- /dev/null +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -0,0 +1,9 @@ +# Ερωτήσεις: + +***Σε ποιες περιπτώσεις μπορεί να σπάσει το render(Component) call μέσα στα tests της React Testing Library;*** + +Some common conditions that might break a simple render(Component) call include: + +1) Missing Dependencies: If the component being rendered relies on external dependencies (e.g., libraries, APIs, or global objects) that are not available in the testing environment, the render call might fail or produce unexpected results. In such cases, you may need to provide mock data or mock functions to simulate the behavior of those dependencies during testing. + +2) Async Operations: If the component relies on asynchronous operations (e.g., fetching data from an API, using `setTimeout`, etc.), the render call might not wait for these operations to complete before asserting the component's behavior. This can lead to test failures or false positives. You should use `waitFor` or other asynchronous testing utilities provided by React Testing Library to handle such cases. From 35145fa60e6a35d1997c583f09d733a8110a599b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:16:20 +0300 Subject: [PATCH 219/227] Update README.md --- .../introduction.to.React.Testing.Library/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index e039bb3..cd9a51f 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -4,6 +4,14 @@ Some common conditions that might break a simple render(Component) call include: -1) Missing Dependencies: If the component being rendered relies on external dependencies (e.g., libraries, APIs, or global objects) that are not available in the testing environment, the render call might fail or produce unexpected results. In such cases, you may need to provide mock data or mock functions to simulate the behavior of those dependencies during testing. +1) **Missing Dependencies**: If the component being rendered relies on external dependencies (e.g., libraries, APIs, or global objects) that are not available in the testing environment, the render call might fail or produce unexpected results. In such cases, you may need to provide mock data or mock functions to simulate the behavior of those dependencies during testing. -2) Async Operations: If the component relies on asynchronous operations (e.g., fetching data from an API, using `setTimeout`, etc.), the render call might not wait for these operations to complete before asserting the component's behavior. This can lead to test failures or false positives. You should use `waitFor` or other asynchronous testing utilities provided by React Testing Library to handle such cases. +2) **Async Operations**: If the component relies on asynchronous operations (e.g., fetching data from an API, using `setTimeout`, etc.), the render call might not wait for these operations to complete before asserting the component's behavior. This can lead to test failures or false positives. You should use `waitFor` or other asynchronous testing utilities provided by React Testing Library to handle such cases. + +3) **Routing**: If the component being tested relies on React Router or any other routing library, the rendering might not work as expected, especially if the routing is not properly configured in the testing environment. [Ensure that you set up your testing environment to support routing if your component relies on it.] + +4) **Context and Providers**: Components might rely on React context or external providers to access specific data or functionalities. If these are not properly set up in the test environment, the component's rendering and behavior could be affected. [Make sure to provide appropriate context or mock providers as needed.] + +5) **Unmounted Components**: If the component being tested performs operations after unmounting (e.g., clearing timers or subscriptions), it might throw errors or warnings when rendered in isolation. [Ensure that you handle cleanup operations properly to avoid test failures.] + +6) **Incorrect Component Tree**: If you render a wrong or incomplete component tree, the behavior might not match your expectations, leading to test failures. [Double-check the component tree being rendered to ensure it is correct for your test scenario.] From b200092fa94b725564b96fb5f67443275f7831d3 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:17:36 +0300 Subject: [PATCH 220/227] Update README.md --- .../README.md | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index cd9a51f..7c055f4 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -1,17 +1,33 @@ # Ερωτήσεις: -***Σε ποιες περιπτώσεις μπορεί να σπάσει το render(Component) call μέσα στα tests της React Testing Library;*** +***Σε ποιες περιπτώσεις μπορεί να σπάσει το `render()` call μέσα στα tests της React Testing Library;*** -Some common conditions that might break a simple render(Component) call include: +Some common conditions that might break a simple `render()` call include: 1) **Missing Dependencies**: If the component being rendered relies on external dependencies (e.g., libraries, APIs, or global objects) that are not available in the testing environment, the render call might fail or produce unexpected results. In such cases, you may need to provide mock data or mock functions to simulate the behavior of those dependencies during testing. 2) **Async Operations**: If the component relies on asynchronous operations (e.g., fetching data from an API, using `setTimeout`, etc.), the render call might not wait for these operations to complete before asserting the component's behavior. This can lead to test failures or false positives. You should use `waitFor` or other asynchronous testing utilities provided by React Testing Library to handle such cases. -3) **Routing**: If the component being tested relies on React Router or any other routing library, the rendering might not work as expected, especially if the routing is not properly configured in the testing environment. [Ensure that you set up your testing environment to support routing if your component relies on it.] +3) **Routing**: If the component being tested relies on React Router or any other routing library, the rendering might not work as expected, especially if the routing is not properly configured in the testing environment. Ensure that you set up your testing environment to support routing if your component relies on it. -4) **Context and Providers**: Components might rely on React context or external providers to access specific data or functionalities. If these are not properly set up in the test environment, the component's rendering and behavior could be affected. [Make sure to provide appropriate context or mock providers as needed.] +4) **Context and Providers**: Components might rely on React context or external providers to access specific data or functionalities. If these are not properly set up in the test environment, the component's rendering and behavior could be affected. Make sure to provide appropriate context or mock providers as needed. -5) **Unmounted Components**: If the component being tested performs operations after unmounting (e.g., clearing timers or subscriptions), it might throw errors or warnings when rendered in isolation. [Ensure that you handle cleanup operations properly to avoid test failures.] +5) **Unmounted Components**: If the component being tested performs operations after unmounting (e.g., clearing timers or subscriptions), it might throw errors or warnings when rendered in isolation. Ensure that you handle cleanup operations properly to avoid test failures. -6) **Incorrect Component Tree**: If you render a wrong or incomplete component tree, the behavior might not match your expectations, leading to test failures. [Double-check the component tree being rendered to ensure it is correct for your test scenario.] +6) **Incorrect Component Tree**: If you render a wrong or incomplete component tree, the behavior might not match your expectations, leading to test failures. Double-check the component tree being rendered to ensure it is correct for your test scenario. + +--- + +**Πότε χρησιμοποιώ το getByTestID στην RTL?** + +You should use `getByTestId` in RTL when: + +1. **Testing Components with Dynamic Content**: When your component's content changes dynamically based on user interactions or data, using `getByTestId` can be more reliable than searching for elements by text content or class names, which might change frequently. + +2. **Testing Components with No Unique Text Content or Class Names**: Some components might not have unique text content or class names that you can reliably target. In such cases, using `getByTestId` allows you to have a stable, test-specific identifier to locate the elements. + +3. **Avoiding Test Fragility**: Relying on specific text content or class names for testing can lead to fragile tests that break easily when small UI changes occur. By using `getByTestId`, you establish a stable and less brittle testing interface. + +4. **Decoupling Testing from Implementation Details**: When testing, you want to focus on the functionality and behavior of your components rather than the specific implementation details. `getByTestId` helps decouple the testing logic from the implementation details, making your tests more maintainable. + +Remember to use `data-testid` attributes judiciously and avoid overusing them, especially when other querying methods like `getByText`, `getByRole`, or `getByPlaceholderText` can serve the purpose effectively. Using `getByTestId` sparingly helps maintain the intent and readability of your tests. From 325a3da4df57b07e73bb1ff83daceec7d37aeccd Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:17:56 +0300 Subject: [PATCH 221/227] Update README.md --- .../youtube/introduction.to.React.Testing.Library/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index 7c055f4..dbc346a 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -31,3 +31,4 @@ You should use `getByTestId` in RTL when: 4. **Decoupling Testing from Implementation Details**: When testing, you want to focus on the functionality and behavior of your components rather than the specific implementation details. `getByTestId` helps decouple the testing logic from the implementation details, making your tests more maintainable. Remember to use `data-testid` attributes judiciously and avoid overusing them, especially when other querying methods like `getByText`, `getByRole`, or `getByPlaceholderText` can serve the purpose effectively. Using `getByTestId` sparingly helps maintain the intent and readability of your tests. + From ac353e8866f7c5b94de77595d28a1cc201e6f4c8 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:19:32 +0300 Subject: [PATCH 222/227] Update README.md --- .../README.md | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index dbc346a..2c9f846 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -1,4 +1,4 @@ -# Ερωτήσεις: +# Ερωτήσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): ***Σε ποιες περιπτώσεις μπορεί να σπάσει το `render()` call μέσα στα tests της React Testing Library;*** @@ -32,3 +32,43 @@ You should use `getByTestId` in RTL when: Remember to use `data-testid` attributes judiciously and avoid overusing them, especially when other querying methods like `getByText`, `getByRole`, or `getByPlaceholderText` can serve the purpose effectively. Using `getByTestId` sparingly helps maintain the intent and readability of your tests. +--- + +**Ποιές είναι μερικές καλές πρακτικές κατά τη χρήση των beforeAll() και beforeEach();** + +To clean up a `render()` call that was included in the `beforeAll()` hook, you need to ensure that you unmount the rendered component after each test to avoid interference between tests. You can achieve this by using the `afterEach()` hook to perform the cleanup. + +In React Testing Library, the `render()` function returns a `RenderResult` object that contains the component, as well as a function called `unmount()`, which can be used to unmount the rendered component and clean up the virtual DOM. + +Here's how you can do it: + +```jsx +describe("App Component", () => { + let renderResult; // Declare a variable to hold the RenderResult + + beforeAll(() => { + renderResult = render(); + }); + + afterEach(() => { + // Unmount the rendered component after each test + renderResult.unmount(); + }); + + test("should display the word 'test'", () => { + // Test logic + // The component is already rendered, and you can interact with it + }); + + test("should increment the count when the button is clicked", () => { + // Test logic + // The component is already rendered, and you can interact with it + }); + + // Add more tests as needed for other scenarios +}); +``` + +By using `renderResult.unmount()` in the `afterEach()` hook, you ensure that the component is unmounted and removed from the virtual DOM after each test is executed. This guarantees a clean slate for each test and prevents any interference or side effects between test cases. + +Remember to always clean up after your tests, especially when rendering components in a `beforeAll()` or `beforeEach()` hook. Failing to do so might lead to test interdependencies or unexpected behavior. From 6e3475a535556dee75f001d81202ad265254f44b Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:19:49 +0300 Subject: [PATCH 223/227] Update README.md --- .../youtube/introduction.to.React.Testing.Library/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index 2c9f846..4a6e293 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -72,3 +72,4 @@ describe("App Component", () => { By using `renderResult.unmount()` in the `afterEach()` hook, you ensure that the component is unmounted and removed from the virtual DOM after each test is executed. This guarantees a clean slate for each test and prevents any interference or side effects between test cases. Remember to always clean up after your tests, especially when rendering components in a `beforeAll()` or `beforeEach()` hook. Failing to do so might lead to test interdependencies or unexpected behavior. + From 61178a7001c9e650345c255abd5100bc45603713 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:21:48 +0300 Subject: [PATCH 224/227] Update README.md --- .../introduction.to.React.Testing.Library/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index 4a6e293..b860da7 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -1,4 +1,12 @@ -# Ερωτήσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): +# Ερωτήσεις και σημειώσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): + +## Resources and References: + +[Setup and Teardown: beforeEach(), afterEach(), beforeAll(), afterAll()](https://jestjs.io/docs/setup-teardown) + +--- + +## Ερωτήσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): ***Σε ποιες περιπτώσεις μπορεί να σπάσει το `render()` call μέσα στα tests της React Testing Library;*** From e5b4f48e96f96ca872905ea2100d78ea0380c7e1 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:22:13 +0300 Subject: [PATCH 225/227] Update README.md --- .../youtube/introduction.to.React.Testing.Library/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index b860da7..600b17c 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -6,7 +6,7 @@ --- -## Ερωτήσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): +## Ερωτήσεις: ***Σε ποιες περιπτώσεις μπορεί να σπάσει το `render()` call μέσα στα tests της React Testing Library;*** From 3fe3750bafaad9a683cf5943e69de157256bf4c4 Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Wed, 23 Aug 2023 14:24:29 +0300 Subject: [PATCH 226/227] Update README.md --- .../youtube/introduction.to.React.Testing.Library/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/youtube/introduction.to.React.Testing.Library/README.md b/resources/youtube/introduction.to.React.Testing.Library/README.md index 600b17c..e0e8466 100644 --- a/resources/youtube/introduction.to.React.Testing.Library/README.md +++ b/resources/youtube/introduction.to.React.Testing.Library/README.md @@ -1,5 +1,7 @@ # Ερωτήσεις και σημειώσεις σχετικές με το βίντεο ["Εισαγωγή στο Testing στην React με React Testing Library"](https://youtu.be/57YuRPiULXc): +Remember: **Always break your tests!** + ## Resources and References: [Setup and Teardown: beforeEach(), afterEach(), beforeAll(), afterAll()](https://jestjs.io/docs/setup-teardown) From 6d97e66b4c364bdd504eddda2b37b07cf27347bf Mon Sep 17 00:00:00 2001 From: Kostas Minaidis Date: Sat, 26 Aug 2023 20:15:10 +0300 Subject: [PATCH 227/227] Added code for useClock video --- .../.codesandbox/workspace.json | 20 ++++++++ .../react.custom.hooks.useClock/package.json | 28 +++++++++++ .../public/index.html | 29 +++++++++++ .../react.custom.hooks.useClock/src/App.js | 49 +++++++++++++++++++ .../react.custom.hooks.useClock/src/index.js | 7 +++ .../src/styles.css | 12 +++++ 6 files changed, 145 insertions(+) create mode 100644 resources/youtube/react.custom.hooks.useClock/.codesandbox/workspace.json create mode 100644 resources/youtube/react.custom.hooks.useClock/package.json create mode 100644 resources/youtube/react.custom.hooks.useClock/public/index.html create mode 100644 resources/youtube/react.custom.hooks.useClock/src/App.js create mode 100644 resources/youtube/react.custom.hooks.useClock/src/index.js create mode 100644 resources/youtube/react.custom.hooks.useClock/src/styles.css diff --git a/resources/youtube/react.custom.hooks.useClock/.codesandbox/workspace.json b/resources/youtube/react.custom.hooks.useClock/.codesandbox/workspace.json new file mode 100644 index 0000000..e7d06a2 --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/.codesandbox/workspace.json @@ -0,0 +1,20 @@ +{ + "responsive-preview": { + "Mobile": [ + 320, + 675 + ], + "Tablet": [ + 1024, + 765 + ], + "Desktop": [ + 1400, + 800 + ], + "Desktop HD": [ + 1920, + 1080 + ] + } +} \ No newline at end of file diff --git a/resources/youtube/react.custom.hooks.useClock/package.json b/resources/youtube/react.custom.hooks.useClock/package.json new file mode 100644 index 0000000..839ac7b --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/package.json @@ -0,0 +1,28 @@ +{ + "name": "react-boilerplate-template", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "src/index.js", + "dependencies": { + "react": "18.1.0", + "react-dom": "18.1.0", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} \ No newline at end of file diff --git a/resources/youtube/react.custom.hooks.useClock/public/index.html b/resources/youtube/react.custom.hooks.useClock/public/index.html new file mode 100644 index 0000000..9272d00 --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/public/index.html @@ -0,0 +1,29 @@ + + + + + + + + + + React App + + + + +
      + + diff --git a/resources/youtube/react.custom.hooks.useClock/src/App.js b/resources/youtube/react.custom.hooks.useClock/src/App.js new file mode 100644 index 0000000..29680fd --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/src/App.js @@ -0,0 +1,49 @@ +import { useEffect, useState } from 'react'; +console.clear(); + +// useClock(); +function getTime(){ + const time = new Date(); + return time.toLocaleString('en-US', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true + }); +} +// console.log( getTime() ); + +function useClock(){ + const [ clock, setClock ] = useState(getTime()); + useEffect(()=>{ + const id = setInterval(()=>{ + console.log("tick"); + setClock(getTime()); + }, 1000 ); + return ()=>{ + clearInterval(id); + } + },[]); + return clock; +} + +function Panel(){ + const clock = useClock(); + return ( +
      Panel { clock }
      + ) +} + +export default function App() { + const [ display, setDisplay ] = useState(true); + return ( + <> +

      React

      + + { display && } + + + ); +} diff --git a/resources/youtube/react.custom.hooks.useClock/src/index.js b/resources/youtube/react.custom.hooks.useClock/src/index.js new file mode 100644 index 0000000..9414660 --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/src/index.js @@ -0,0 +1,7 @@ +import { createRoot } from "react-dom/client"; +import "./styles.css"; +import App from "./App"; + +const rootElement = createRoot(document.getElementById("root")); +rootElement.render( ); + diff --git a/resources/youtube/react.custom.hooks.useClock/src/styles.css b/resources/youtube/react.custom.hooks.useClock/src/styles.css new file mode 100644 index 0000000..4fcaf92 --- /dev/null +++ b/resources/youtube/react.custom.hooks.useClock/src/styles.css @@ -0,0 +1,12 @@ +body { + width: 100vw; + min-height: 100vh; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + display: flex; + justify-content: center; + align-items: center; + font-size: 28px; + background: #00d8ff; + color: white; + overflow: hidden; +} \ No newline at end of file
  • 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